Implemented dynamic playlists
authorNikolay Tischenko <niktischenko@gmail.com>
Wed, 15 Sep 2010 17:47:22 +0000 (00:47 +0700)
committerNikolay Tischenko <niktischenko@gmail.com>
Wed, 15 Sep 2010 17:47:22 +0000 (00:47 +0700)
Improved current playlist view
Minor bugfixing

22 files changed:
someplayer.pro
src/dbstorage.cpp
src/dbstorage.h
src/filestorage.cpp
src/filestorage.h
src/library.cpp
src/library.h
src/libraryform.cpp
src/libraryform.h
src/mainwindow.cpp
src/mainwindow.h
src/player/player.cpp
src/player/player.h
src/playerform.cpp
src/playerform.h
src/playlist.cpp
src/playlist.h
src/track.cpp
src/trackrenderer.cpp [new file with mode: 0644]
src/trackrenderer.h [new file with mode: 0644]
src/ui/libraryform.ui
src/ui/playerform.ui

index 5c081bb..e66292f 100644 (file)
@@ -23,7 +23,8 @@ SOURCES += src/main.cpp\
     src/tagresolver.cpp \
     src/playerform.cpp \
     src/libraryform.cpp \
-    src/busywidget.cpp
+    src/busywidget.cpp \
+    src/trackrenderer.cpp
 
 HEADERS  += src/mainwindow.h \
                src/player/player.h \
@@ -39,7 +40,8 @@ HEADERS  += src/mainwindow.h \
     src/tagresolver.h \
     src/playerform.h \
     src/libraryform.h \
-    src/busywidget.h
+    src/busywidget.h \
+    src/trackrenderer.h
 
 FORMS    += src/ui/mainwindow.ui \
     src/ui/playerform.ui \
index 429e00c..11a551a 100644 (file)
@@ -57,6 +57,10 @@ void DbStorage::_prepare_queries() {
                                                                           "JOIN album ON album_id = album.id "
                                                                           "WHERE track_id IN "
                                                                           "(SELECT track_id FROM adding_date ORDER BY date DESC LIMIT 0, :max)");
+
+       _get_track_count = new QSqlQuery(db);
+       _get_track_count->prepare("SELECT count from tracks WHERE id = :id");
+
        _check_artist_query = new QSqlQuery(db);
        _check_artist_query->prepare("SELECT id FROM artist WHERE name = :name");
 
@@ -333,10 +337,16 @@ void DbStorage::addToFavorites(Track track) {
 }
 
 void DbStorage::updateTrackCount(Track track) {
-       QSqlQuery *query = _update_track_count_query;
-       query->bindValue(":count", track.count());
+       QSqlQuery *query = _get_track_count;
        query->bindValue(":id", track.id());
        query->exec();
+       if (query->next()) {
+               int count = query->value(0).toInt();
+               query = _update_track_count_query;
+               query->bindValue(":count", count+1);
+               query->bindValue(":id", track.id());
+               query->exec();
+       }
 }
 
 int DbStorage::_check_add_artist(QString artist) {
index 34c4afe..140e169 100644 (file)
@@ -54,6 +54,7 @@ namespace SomePlayer {
                        QSqlQuery *_get_most_played_query;
                        QSqlQuery *_get_never_played_query;
                        QSqlQuery *_get_recently_added_query;
+                       QSqlQuery *_get_track_count;
 
                        QSqlQuery *_check_artist_query;
                        QSqlQuery *_check_album_query;
index f1e5222..93c7261 100644 (file)
@@ -11,7 +11,7 @@ using namespace SomePlayer::DataObjects;
 
 FileStorage::FileStorage(QString path) {
        _path_prefix = path;
-       _meta_regexp.setPattern("#META \\[(\\d+)\\].*::(.+)::,::(.+)::,::(.+)::");
+       _meta_regexp.setPattern("#META\\ +\\[(\\d+)\\]\\[(\\d+)\\].*::(.+)::,::(.+)::,::(.+)::");
        _path_regexp.setPattern("#PATH (.+)");
 
        Playlist current = getCurrentPlaylist();
@@ -35,20 +35,20 @@ Playlist FileStorage::getPlaylist(QString name) {
                playlistFile.open(QFile::ReadOnly);
                QTextStream stream(&playlistFile);
                QString buffer = stream.readLine();
-               int index = 0;
                if (buffer.startsWith(_PLAYLIST_SIGNATURE_)) {
                        while (!stream.atEnd()) {
                                buffer = stream.readLine();
                                if (_meta_regexp.indexIn(buffer) != -1) {
-                                       int seconds = _meta_regexp.cap(1).toInt();
-                                       QString artist = _meta_regexp.cap(2);
-                                       QString album = _meta_regexp.cap(3);
-                                       QString title = _meta_regexp.cap(4);
+                                       int id = _meta_regexp.cap(1).toInt();
+                                       int seconds = _meta_regexp.cap(2).toInt();
+                                       QString artist = _meta_regexp.cap(3);
+                                       QString album = _meta_regexp.cap(4);
+                                       QString title = _meta_regexp.cap(5);
                                        buffer = stream.readLine();
                                        if (_path_regexp.indexIn(buffer) != -1) {
                                                QString source = _path_regexp.cap(1);
                                                TrackMetadata meta(title, artist, album, seconds);
-                                               Track track(index++, meta, source);
+                                               Track track(id, meta, source);
                                                playlist.addTrack(track);
                                        }
                                }
@@ -85,8 +85,9 @@ void FileStorage::savePlaylist(Playlist playlist) {
        stream << _PLAYLIST_SIGNATURE_ << endl;
        const QList<Track> &tracks = playlist.tracks();
        foreach (Track track, tracks) {
-               stream << _PLAYLIST_META_KEYWORD_ << " [" << track.metadata().length() << "],::" << track.metadata().artist()
-                               << "::,::" << track.metadata().album() << "::,::" << track.metadata().title() << "::" << endl;
+               stream << _PLAYLIST_META_KEYWORD_ << " [" << track.id() << "]" << "[" << track.metadata().length() << "],::"
+                               << track.metadata().artist() << "::,::" << track.metadata().album() << "::,::"
+                               << track.metadata().title() << "::" << endl;
                stream << _PLAYLIST_PATH_KEYWORD_ << " " << track.source() << endl;
        }
 }
index e8b8a23..d49216f 100644 (file)
@@ -15,9 +15,9 @@
 // format:
 /*
  #SOMEPLAYLIST
- #META [seconds],::artist::,::album::,::title::
+ #META [ID][seconds],::artist::,::album::,::title::
  #PATH file_path
- #META [seconds],::artist::,::album::,::title::
+ #META [ID][seconds],::artist::,::album::,::title::
  #PATH file_path
  ...
  */
index 1a34860..cde9a91 100644 (file)
@@ -12,7 +12,7 @@ Library::Library(QString databasePath, QString playlistsPath) : QObject(0) {
        _scanner = new MediaScanner();
        _resolver = new TagResolver(this);
        connect(_scanner, SIGNAL(scanFinish(QStringList)), _resolver, SLOT(decode(QStringList)));
-       connect(_resolver, SIGNAL(done()), this, SIGNAL(addingDone()));
+       connect(_resolver, SIGNAL(done()), this, SIGNAL(done()));
        connect(_resolver, SIGNAL(decoded(Track)), this, SLOT(addTrack(Track)));
 }
 
index 742c03e..251d42f 100644 (file)
@@ -54,7 +54,8 @@ namespace SomePlayer {
                        void saveCurrentPlaylist(const Playlist &playlist);
 
                signals:
-                       void addingDone();
+                       void done();
+                       void busy(QString);
 
                private:
                        DbStorage *_library_storage;
index 7df57a6..f2e335b 100644 (file)
@@ -50,10 +50,12 @@ LibraryForm::LibraryForm(Library *lib, QWidget *parent) :
        connect(ui->playerButton, SIGNAL(clicked()), this, SLOT(_player()));
        connect(ui->viewButton, SIGNAL(clicked()), this, SLOT(_view_button()));
        connect(ui->playlistsButton, SIGNAL(clicked()), this, SLOT(_playlists_button()));
+       connect(ui->dynamicButton, SIGNAL(clicked()), this, SLOT(_dynamic_button()));
        connect(ui->listView, SIGNAL(clicked(QModelIndex)), this, SLOT(_process_list_click(QModelIndex)));
        connect(ui->addButton, SIGNAL(clicked()), this, SLOT(_add_button()));
        connect(ui->backButton, SIGNAL(clicked()), this, SLOT(_back_button()));
        connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(_delete_button()));
+       connect(ui->useButton, SIGNAL(clicked()), this, SLOT(_use_button()));
        _view_button();
 }
 
@@ -75,10 +77,21 @@ void LibraryForm::_view_button() {
        ui->backButton->setEnabled(false);
        ui->listLabel->setText("Artists");
        ui->addButton->setEnabled(true);
-       ui->deleteButton->setEnabled(false);
+       ui->deleteButton->hide();
+       ui->useButton->hide();
 }
 
 void LibraryForm::_dynamic_button() {
+       ui->useButton->hide();
+       ui->backButton->setEnabled(false);
+       ui->addButton->setEnabled(true);
+       _model->clear();
+       _model->setRowCount(4);
+       _model->setItem(0, new QStandardItem("Favorites"));
+       _model->setItem(1, new QStandardItem("Most played"));
+       _model->setItem(2, new QStandardItem("Never played"));
+       _model->setItem(3, new QStandardItem("Recently added"));
+       _state = STATE_DYNAMIC;
 }
 
 void LibraryForm::_process_list_click(QModelIndex index) {
@@ -102,15 +115,40 @@ void LibraryForm::_process_list_click(QModelIndex index) {
                break;
        case STATE_PLAYLIST:
                {
-                       Playlist playlist = _lib->getPlaylist(data);
-                       _current_tracks = playlist.tracks();
+                       _current_playlist = _lib->getPlaylist(data);
+                       _current_tracks = _current_playlist.tracks();
                        __fill_model_tracks(_model, _current_tracks);
                        _state = STATE_PLAYLIST_TRACK;
                        ui->backButton->setEnabled(true);
-                       ui->deleteButton->setEnabled(true);
+                       ui->deleteButton->show();
+                       ui->useButton->show();
                        ui->listLabel->setText(QString("Tracks in playlist \"%1\"").arg(data));
                }
                break;
+       case STATE_DYNAMIC:
+               {
+                       switch(index.row()) {
+                       case 0: //favorites
+                               _current_playlist = _lib->getFavorites();
+                               break;
+                       case 1: //most played
+                               _current_playlist = _lib->getMostPlayed();
+                               break;
+                       case 2: //never played
+                               _current_playlist = _lib->getNeverPlayed();
+                       case 3: //recently added
+                               _current_playlist = _lib->getRecentlyAdded();
+                               break;
+                       default:
+                               return;
+                       }
+                       _current_tracks = _current_playlist.tracks();
+                       __fill_model_tracks(_model, _current_tracks);
+                       _state = STATE_PLAYLIST_TRACK;
+                       ui->backButton->setEnabled(true);
+                       ui->useButton->show();
+                       ui->listLabel->setText(_current_playlist.name());
+               }
        default:
                return;
        }
@@ -120,6 +158,7 @@ void LibraryForm::_add_button() {
        if (_state == STATE_NONE) return;
        QModelIndexList selected = ui->listView->selectionModel()->selectedIndexes();
        ui->listView->selectionModel()->clearSelection();
+       emit busy(QString("<H1>Adding... Please wait</H1>"));
        switch (_state) {
        case STATE_ARTIST:
                foreach (QModelIndex id, selected) {
@@ -147,8 +186,10 @@ void LibraryForm::_add_button() {
                }
                break;
        default:
+               emit done();
                return;
        }
+       emit done();
 }
 
 
@@ -205,6 +246,8 @@ void LibraryForm::_playlists_button() {
        ui->backButton->setEnabled(false);
        ui->listLabel->setText("Playlists");
        ui->addButton->setEnabled(false);
+       ui->deleteButton->hide();
+       ui->useButton->hide();
 }
 
 void LibraryForm::_delete_button() {
@@ -230,3 +273,7 @@ void LibraryForm::_delete_track(Track track) {
        current.removeTrack(track);
        _lib->saveCurrentPlaylist(current);
 }
+
+void LibraryForm::_use_button() {
+       _lib->saveCurrentPlaylist(_current_playlist);
+}
index b842778..38cf8cd 100644 (file)
@@ -7,6 +7,7 @@
 #include <QStandardItemModel>
 #include <QStandardItem>
 #include <QModelIndex>
+#include "playlist.h"
 
 namespace Ui {
     class LibraryForm;
@@ -16,7 +17,7 @@ using SomePlayer::DataObjects::Library;
 using SomePlayer::DataObjects::Track;
 using SomePlayer::DataObjects::Playlist;
 
-enum LibraryFormListState {STATE_NONE, STATE_ARTIST, STATE_ALBUM, STATE_TRACK, STATE_PLAYLIST, STATE_PLAYLIST_TRACK};
+enum LibraryFormListState {STATE_NONE, STATE_ARTIST, STATE_ALBUM, STATE_TRACK, STATE_PLAYLIST, STATE_PLAYLIST_TRACK, STATE_DYNAMIC};
 
 class LibraryForm : public QWidget
 {
@@ -27,6 +28,8 @@ public:
     ~LibraryForm();
 signals:
        void player();
+       void busy(QString);
+       void done();
 private slots:
        void _player();
        void _view_button();
@@ -35,6 +38,7 @@ private slots:
        void _add_button();
        void _delete_button();
        void _back_button();
+       void _use_button();
        void _process_list_click(QModelIndex);
 
 private:
@@ -44,6 +48,7 @@ private:
        LibraryFormListState _state;
        QString _current_artist;
        QString _current_album;
+       Playlist _current_playlist;
        QList<Track> _current_tracks;
 
        void _add_artist(QString artist);
index 32421f3..3617517 100644 (file)
@@ -2,6 +2,7 @@
 #include "ui_mainwindow.h"
 #include <QFileDialog>
 #include <QMessageBox>
+#include <QInputDialog>
 #include <QFile>
 
 #include "player/player.h"
@@ -30,11 +31,17 @@ MainWindow::MainWindow(QWidget *parent) :
        ui->stackedWidget->insertWidget(2, _busy_widget);
        _library_menu = new QMenu("Lirary");
        QAction *add_directory = _library_menu->addAction("Add directory");
+       QAction *save_playlist = _library_menu->addAction("Save playlist");
+       QAction *clear_playlist = _library_menu->addAction("Clear current playlist");
        _player_menu = new QMenu("Player");
        connect(_player_form, SIGNAL(library()), this, SLOT(library()));
        connect(_library_form, SIGNAL(player()), this, SLOT(player()));
        connect(add_directory, SIGNAL(triggered()), this, SLOT(_add_directory()));
-       connect(_library, SIGNAL(addingDone()), this, SLOT(library()));
+       connect(save_playlist, SIGNAL(triggered()), this, SLOT(_save_playlist()));
+       connect(clear_playlist, SIGNAL(triggered()), this, SLOT(_clear_current_playlist()));
+       connect(_library, SIGNAL(done()), this, SLOT(library()));
+       connect(_library_form, SIGNAL(done()), this, SLOT(library()));
+       connect(_library_form, SIGNAL(busy(QString)), this, SLOT(showBusyWidget(QString)));
        library();
 }
 
@@ -77,9 +84,27 @@ void MainWindow::library() {
 void MainWindow::_add_directory() {
        QString directory = QFileDialog::getExistingDirectory (this, "Select directory", "/home/user/MyDocs", QFileDialog::ShowDirsOnly );
        if (!directory.isEmpty()) {
-               _busy_widget->setText("<H1>Scanning... Please wait</H1>");
-               ui->menuBar->setEnabled(false);
-               ui->stackedWidget->setCurrentIndex(2);
+               showBusyWidget("<H1>Scanning... Please wait</H1>");
                _library->addDirectory(directory);
        }
 }
+
+void MainWindow::_save_playlist() {
+       QString name = QInputDialog::getText(this, "Playlist name", "Name:");
+       Playlist playlist = _library->getCurrentPlaylist();
+       playlist.setName(name);
+       _library->savePlaylist(playlist);
+}
+
+void MainWindow::_clear_current_playlist() {
+       Playlist playlist = _library->getCurrentPlaylist();
+       playlist.clear();
+       _library->saveCurrentPlaylist(playlist);
+       _player_form->reload();
+}
+
+void MainWindow::showBusyWidget(QString caption) {
+       _busy_widget->setText(caption);
+       ui->menuBar->setEnabled(false);
+       ui->stackedWidget->setCurrentIndex(2);
+}
index 24f8eff..a229394 100644 (file)
@@ -32,8 +32,11 @@ public slots:
        void about();
        void player();
        void library();
+       void showBusyWidget(QString);
 private slots:
        void _add_directory();
+       void _save_playlist();
+       void _clear_current_playlist();
 private:
        PlayerForm *_player_form;
        LibraryForm *_library_form;
index 10c3855..7eecd0e 100644 (file)
@@ -14,23 +14,30 @@ Player::Player(QObject *parent) :
        connect(_player, SIGNAL(tick(qint64)), this, SLOT(_tick(qint64)));
        Phonon::createPath(_player, _output);
        int seed = reinterpret_cast<int> (_player);
-       srand(seed);
+       qsrand(seed);
        _random = false;
        _repeat = false;
 }
 
 void Player::setTrackId(int id) {
        _current = id;
-       _history.push(_current);
+       if (!_history.isEmpty() && _history.top() != _current || _history.isEmpty()) {
+               _history.push(_current);
+       }
+       _track = _playlist.tracks().at(_current);
        _set_source();
        _state = PLAYER_LOADING;
        emit stateChanged(_state);
 }
 
-void Player::play() {
-       _player->play();
-       _state = PLAYER_PLAYING;
-       emit stateChanged(_state);
+void Player::toggle() {
+       if (_state == PLAYER_PLAYING) { // pause
+               _player->pause();
+               _state = PLAYER_PAUSED;
+               emit stateChanged(_state);
+       } else { //play
+               play();
+       }
 }
 
 void Player::stop() {
@@ -39,37 +46,45 @@ void Player::stop() {
        emit stateChanged(_state);
 }
 
-void Player::pause() {
-       _player->pause();
-       _state = PLAYER_PAUSED;
-       emit stateChanged(_state);
-}
-
 void Player::next() {
-       _history.push(_current);
-       if (_random) {
-               _current = rand() % _playlist.tracks().count();
+       int count = _playlist.tracks().count();
+       if (count == 0) {
+               stop(); // empty playlist
+               return;
+       }
+       _history.push(_current % count);
+       if (!_queue.isEmpty()) {
+               _current = _queue.dequeue();
        } else {
-               _current = (_current + 1) % _playlist.tracks().count();
+               if (_random) {
+                       _current = (count + (qrand()  + qrand() + qrand()) % count) % count;
+               } else {
+                       _current = _current + 1;
+               }
        }
-       if (_history.count()-1 == _playlist.tracks().count() && !_repeat) {
+       if (_random && _history.count() >= count && !_repeat ||
+               !_repeat && _current >= count) {
                _history.clear();
                stop();
        } else {
+               _current %= count;
+               _track = _playlist.tracks().at(_current);
                _set_source();
                play();
        }
 }
 
 void Player::_set_source() {
-       Track track = _playlist.tracks().at(_current);
-       _player->setCurrentSource(Phonon::MediaSource(track.source()));
-       emit trackChanged(track);
+       _player->setCurrentSource(Phonon::MediaSource(_track.source()));
+       emit trackChanged(_track);
 }
 
 void Player::prev() {
-       if (_history.count() > 0)
+       if (_history.count() > 0) {
+               _queue.push_front(_current);
                _current = _history.pop();
+               _track = _playlist.tracks().at(_current);
+       }
        _set_source();
        play();
 }
@@ -103,7 +118,13 @@ void Player::_stateChanged(Phonon::State newState, Phonon::State oldState) {
 }
 
 void Player::_tick(qint64 ticks) {
-       emit tick(ticks/1000, _playlist.tracks().at(_current).metadata().length());
+       int done = ticks/1000;
+       int all = _track.metadata().length();
+       emit tick(done, all);
+       if (done+2 == all) {
+               _track.setCount(_track.count()+1);
+               emit trackDone(_track);
+       }
 }
 
 void Player::setPlaylist(Playlist playlist) {
@@ -114,3 +135,13 @@ void Player::setPlaylist(Playlist playlist) {
 void Player::seek(int s) {
        _player->seek(s*1000);
 }
+
+void Player::play() {
+       _player->play();
+       _state = PLAYER_PLAYING;
+       emit stateChanged(_state);
+}
+
+void Player::enqueue(int id) {
+       _queue.enqueue(id);
+}
index ec3d82d..e381838 100644 (file)
@@ -9,6 +9,7 @@
 #include <phonon/MediaObject>
 #include <phonon/AudioOutput>
 #include <QStack>
+#include <QQueue>
 
 // represents player
 
@@ -35,11 +36,13 @@ namespace SomePlayer {
                        void stateChanged (PlayerState);
                        void trackChanged (Track);
                        void tick (int, int); // played | all (seconds)
+                       void trackDone(Track);
 
                public slots:
                        void setTrackId(int id);
+                       void enqueue(int id);
+                       void toggle();
                        void play();
-                       void pause();
                        void stop();
                        void next();
                        void prev();
@@ -52,15 +55,18 @@ namespace SomePlayer {
                        void _tick(qint64);
                private:
                        int _current;
+                       Track _track; // current track (workaround)
                        bool _random;
                        bool _repeat;
                        QStack<int> _history;
+                       QQueue<int> _queue;
                        Playlist _playlist;
                        Phonon::MediaObject *_player;
                        Phonon::AudioOutput *_output;
                        PlayerState _state;
 
                        void _set_source();
+
                };
        };
 };
index a1af42f..d0bb5dc 100644 (file)
@@ -4,6 +4,7 @@
 #include <QDebug>
 #include <QTime>
 #include <QSlider>
+#include "trackrenderer.h"
 
 using namespace SomePlayer::DataObjects;
 using namespace SomePlayer::Playback;
@@ -15,8 +16,8 @@ inline void __fill_list(QStandardItemModel *_model, Playlist playlist) {
        _model->setRowCount(count);
        for (int i = 0; i < count; i++) {
                TrackMetadata meta = tracks.at(i).metadata();
-               _model->setItem(i, 0, new QStandardItem(meta.title()));
-               _model->setItem(i, 1, new QStandardItem(meta.artist()));
+               QString t = meta.title()+"#_#"+meta.artist()+"#_#"+meta.album();
+               _model->setItem(i, 0, new QStandardItem(t));
        }
 }
 
@@ -30,19 +31,34 @@ PlayerForm::PlayerForm(Library* lib, QWidget *parent) :
        connect(ui->libraryButton, SIGNAL(clicked()), this, SLOT(_library()));
        connect(ui->viewButton, SIGNAL(clicked()), this, SLOT(_toggle_view()));
        connect(ui->playlistView, SIGNAL(clicked(QModelIndex)), this, SLOT(_process_click(QModelIndex)));
-       connect(ui->playButton, SIGNAL(clicked()), _player, SLOT(play()));
-       connect(ui->pauseButton, SIGNAL(clicked()), _player, SLOT(pause()));
+       connect(ui->playpauseButton, SIGNAL(clicked()), _player, SLOT(toggle()));
        connect(ui->stopButton, SIGNAL(clicked()), _player, SLOT(stop()));
        connect(ui->nextButton, SIGNAL(clicked()), _player, SLOT(next()));
        connect(ui->prevButton, SIGNAL(clicked()), _player, SLOT(prev()));
        connect(_player, SIGNAL(trackChanged(Track)), this, SLOT(_track_changed(Track)));
        connect(_player, SIGNAL(tick(int,int)), this, SLOT(_tick(int,int)));
+       connect(ui->randomButton, SIGNAL(clicked()), _player, SLOT(toggleRandom()));
+       connect(ui->repeatButton, SIGNAL(clicked()), _player, SLOT(toggleRepeat()));
+       ui->randomButton->setChecked(_player->random());
+       ui->repeatButton->setChecked(_player->repeat());
        _seek_slider = new QSlider(Qt::Horizontal);
        ui->progressLayout->insertWidget(1, _seek_slider);
        _seek_slider->setTracking(false);
-       connect(_seek_slider, SIGNAL(sliderMoved(int)), _player, SLOT(seek(int)));
+       connect(_seek_slider, SIGNAL(sliderReleased()), this, SLOT(_slider_released()));
+       connect(ui->playlistView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(_custom_context_venu_requested(QPoint)));
        _model = new QStandardItemModel(0, 2, this);
        ui->playlistView->setModel(_model);
+       _context_menu = new QMenu(ui->playlistView);
+       QAction *delete_action = _context_menu->addAction("Delete");
+       connect(delete_action, SIGNAL(triggered()), this, SLOT(_delete_track()));
+       QAction *enqueue_action = _context_menu->addAction("Enqueue");
+       connect(enqueue_action, SIGNAL(triggered()), this, SLOT(_enqueue_track()));
+       QAction *add_to_favorites = _context_menu->addAction("Add to favorites");
+       connect(add_to_favorites, SIGNAL(triggered()), this, SLOT(_add_to_favorites()));
+
+       _track_renderer = new TrackRenderer(this);
+       ui->playlistView->setItemDelegateForColumn(0, _track_renderer);
+       connect(_player, SIGNAL(trackDone(Track)), _lib, SLOT(updateTrackCount(Track)));
 }
 
 PlayerForm::~PlayerForm()
@@ -71,12 +87,19 @@ void PlayerForm::_process_click(QModelIndex index) {
        _player->stop();
        _player->setTrackId(id);
        _player->play();
+       _track_renderer->setActiveRow(id);
+       ui->playlistView->hide();
+       ui->playlistView->show();
 }
 
 void PlayerForm::_track_changed(Track track) {
        int id = _current_playlist.tracks().indexOf(track);
        QModelIndex index = _model->index(id, 0);
        ui->playlistView->setCurrentIndex(index);
+       ui->playlistView->scrollTo(index);
+       _track_renderer->setActiveRow(id);
+       ui->playlistView->hide();
+       ui->playlistView->show();
        _display_track(track);
 }
 
@@ -90,16 +113,41 @@ void PlayerForm::_display_track(Track track) {
                                                                  arg(track.metadata().album()));
        _seek_slider->setMinimum(0);
        _seek_slider->setMaximum(track.metadata().length());
+       _tick(0, track.metadata().length());
 }
 
 void PlayerForm::_tick(int done, int all) {
-       QTime time1(0, all/60, all%60);
-       QTime time2(0, done/60, done%60);
-       ui->allTimeLabel->setText(time1.toString("mm:ss"));
-       ui->doneTimeLabel->setText(time2.toString("mm:ss"));
+       QTime time(0, all/60, all%60);
+       ui->allTimeLabel->setText(time.toString("mm:ss"));
+       time.setHMS(0, done/60, done%60);
+       ui->doneTimeLabel->setText(time.toString("mm:ss"));
        _seek_slider->setValue(done);
 }
 
 void PlayerForm::_slider_released() {
        _player->seek(_seek_slider->value());
 }
+
+void PlayerForm::_custom_context_venu_requested(const QPoint &pos) {
+       _context_menu->exec(pos);
+}
+
+void PlayerForm::_delete_track() {
+       QList<QModelIndex> idx = ui->playlistView->selectionModel()->selectedIndexes();
+       int id = idx.first().row();
+       _current_playlist.removeTrackAt(id);
+       _lib->saveCurrentPlaylist(_current_playlist);
+       reload();
+}
+
+void PlayerForm::_enqueue_track() {
+       QList<QModelIndex> idx = ui->playlistView->selectionModel()->selectedIndexes();
+       int id = idx.first().row();
+       _player->enqueue(id);
+}
+
+void PlayerForm::_add_to_favorites() {
+       QList<QModelIndex> idx = ui->playlistView->selectionModel()->selectedIndexes();
+       int id = idx.first().row();
+       _lib->addToFavorites(_current_playlist.tracks().at(id));
+}
index 91c1e3f..335d3fa 100644 (file)
@@ -7,6 +7,8 @@
 #include <QStandardItemModel>
 #include "player/player.h"
 #include <QSlider>
+#include <QMenu>
+#include "trackrenderer.h"
 
 namespace Ui {
     class PlayerForm;
@@ -37,6 +39,10 @@ private slots:
        void _track_changed(Track);
        void _tick(int, int);
        void _slider_released();
+       void _custom_context_venu_requested ( const QPoint & pos );
+       void _delete_track();
+       void _enqueue_track();
+       void _add_to_favorites();
 
 private:
     Ui::PlayerForm *ui;
@@ -45,7 +51,9 @@ private:
        QStandardItemModel *_model;
        Player *_player;
        QSlider *_seek_slider;
+       QMenu *_context_menu;
 
+       TrackRenderer *_track_renderer;
        void _display_track(Track);
 };
 
index 417ac26..4704c3b 100644 (file)
@@ -34,3 +34,10 @@ void Playlist::removeTrack(Track track) {
        _tracks.removeOne(track);
 }
 
+void Playlist::removeTrackAt(int id) {
+       _tracks.removeAt(id);
+}
+
+void Playlist::clear() {
+       _tracks.clear();
+}
index 21ba765..44ab822 100644 (file)
@@ -22,6 +22,8 @@ namespace SomePlayer {
                        void setName(QString name);
                        void addTrack(Track track);
                        void removeTrack(Track track);
+                       void removeTrackAt(int id);
+                       void clear();
 
                private:
                        QString _name;
index c61a07a..e02a59e 100644 (file)
@@ -10,11 +10,14 @@ Track::Track(int id, TrackMetadata metadata, QString source) : QObject() {
        _id = id;
        _metadata = metadata;
        _source = source;
+       _count = 0;
 }
 
 Track::Track(const Track &track) : QObject() {
        this->_metadata = track.metadata();
        this->_source = track.source();
+       this->_id = track._id;
+       this->_count = track._count;
 }
 
 Track::Track(QString source) :QObject() {
@@ -23,6 +26,8 @@ Track::Track(QString source) :QObject() {
        QStringList foo;
        foo << source;
        _resolver->decode(foo);
+       _count = 0;
+       _id = 0;
 }
 
 TrackMetadata Track::metadata() const {
@@ -60,6 +65,7 @@ Track &Track::operator =(const Track &track) {
        _id = track.id();
        _source = track.source();
        _metadata = track.metadata();
+       _count = track._count;
        return *this;
 }
 
diff --git a/src/trackrenderer.cpp b/src/trackrenderer.cpp
new file mode 100644 (file)
index 0000000..2d88cb1
--- /dev/null
@@ -0,0 +1,56 @@
+#include "trackrenderer.h"
+#include <QFont>
+#include <QFontMetrics>
+#include <QSize>
+#include <QColor>
+
+TrackRenderer::TrackRenderer(QObject *parent) :
+    QAbstractItemDelegate(parent)
+{
+}
+
+void TrackRenderer::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
+       QString value = index.data().toString();
+       QStringList meta = value.split("#_#");
+
+       QFont f = painter->font();
+       QFont fp = painter->font();
+
+       int x1, y1, x2, y2;
+       option.rect.getCoords(&x1, &y1, &x2, &y2);
+
+       QPen pen = painter->pen();
+       QPen npen (QColor::fromRgb(80, 130, 255, 50));
+       QPen apen (QColor::fromRgb(255, 255, 255, 128));
+       QPen spen (QColor::fromRgb(100, 150, 220));
+
+       f.setBold(true);
+       painter->setFont(f);
+       f.setBold(false);
+       painter->setPen(npen);
+       painter->drawLine(x1, y1, x2, y1);
+       if (index.row() == _active_row) {
+               painter->setPen(spen);
+       } else {
+               painter->setPen(pen);
+       }
+       painter->drawText(x1, y1 + 2*(y2-y1)/5, meta[0]);
+       fp.setBold(false);
+       fp.setPointSize(f.pointSize()*3/4);
+       painter->setFont(fp);
+       painter->setPen(apen);
+       painter->drawText((x2+x1)/2, y1 + 4*(y2-y1)/6, meta[1]);
+       painter->drawText((x2+x1)/2, y1 + 11*(y2-y1)/12, meta[2]);
+       painter->setPen(npen);
+       painter->drawLine(x1, y2, x2, y2);
+       painter->setFont(f);
+       painter->setPen(pen);
+}
+
+QSize TrackRenderer::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
+       return QSize(option.rect.width(), option.rect.height()/3);
+}
+
+void TrackRenderer::setActiveRow(int r) {
+       _active_row = r;
+}
diff --git a/src/trackrenderer.h b/src/trackrenderer.h
new file mode 100644 (file)
index 0000000..c18a187
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef TRACKRENDERER_H
+#define TRACKRENDERER_H
+
+#include <QAbstractItemDelegate>
+#include <QPainter>
+
+class TrackRenderer : public QAbstractItemDelegate
+{
+    Q_OBJECT
+public:
+    explicit TrackRenderer(QObject *parent = 0);
+       void paint(QPainter *painter, const QStyleOptionViewItem &option,
+                                                               const QModelIndex &index) const;
+
+       QSize sizeHint(const QStyleOptionViewItem &option,
+                                                  const QModelIndex &index) const;
+
+signals:
+
+public slots:
+       void setActiveRow(int);
+private:
+       int _active_row;
+
+};
+
+#endif // TRACKRENDERER_H
index 7d9f819..276cbc2 100644 (file)
         </widget>
        </item>
        <item>
+        <spacer name="verticalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QPushButton" name="useButton">
+         <property name="text">
+          <string>Use</string>
+         </property>
+         <property name="flat">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
         <spacer name="verticalSpacer_3">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
index c552cb1..f26d3bc 100644 (file)
    <string>Form</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
-   <property name="spacing">
-    <number>0</number>
-   </property>
-   <property name="margin">
-    <number>0</number>
-   </property>
-   <item>
-    <widget class="QStackedWidget" name="stackedWidget">
-     <property name="currentIndex">
-      <number>1</number>
-     </property>
-     <widget class="QWidget" name="page">
-      <layout class="QVBoxLayout" name="verticalLayout_4">
-       <property name="spacing">
-        <number>0</number>
-       </property>
-       <property name="margin">
-        <number>0</number>
-       </property>
-       <item>
-        <widget class="QListView" name="playlistView">
-         <property name="editTriggers">
-          <set>QAbstractItemView::NoEditTriggers</set>
-         </property>
-         <property name="textElideMode">
-          <enum>Qt::ElideLeft</enum>
-         </property>
-         <property name="uniformItemSizes">
-          <bool>true</bool>
-         </property>
-         <property name="wordWrap">
-          <bool>true</bool>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-     <widget class="QWidget" name="page_2">
-      <layout class="QGridLayout" name="gridLayout">
-       <item row="0" column="0">
-        <widget class="QWidget" name="widget" native="true">
-         <layout class="QVBoxLayout" name="verticalLayout_2">
-          <item>
-           <layout class="QHBoxLayout" name="horizontalLayout_5">
-            <item>
-             <spacer name="horizontalSpacer_2">
-              <property name="orientation">
-               <enum>Qt::Horizontal</enum>
-              </property>
-              <property name="sizeHint" stdset="0">
-               <size>
-                <width>40</width>
-                <height>20</height>
-               </size>
-              </property>
-             </spacer>
-            </item>
-            <item>
-             <widget class="QLabel" name="countLabel">
-              <property name="text">
-               <string>0/0</string>
-              </property>
-              <property name="alignment">
-               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-              </property>
-             </widget>
-            </item>
-           </layout>
-          </item>
-          <item>
-           <widget class="QLabel" name="titleLabel">
-            <property name="text">
-             <string>&lt;h3&gt;Title&lt;/h3&gt;</string>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignCenter</set>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <layout class="QHBoxLayout" name="progressLayout">
-            <item>
-             <widget class="QLabel" name="doneTimeLabel">
-              <property name="text">
-               <string>00:00</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QLabel" name="allTimeLabel">
-              <property name="text">
-               <string>00:00</string>
-              </property>
-             </widget>
-            </item>
-           </layout>
-          </item>
-          <item>
-           <widget class="QLabel" name="artistAlbumLabel">
-            <property name="text">
-             <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
-p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:large; font-weight:600;&quot;&gt;Artist&lt;/span&gt;&lt;/p&gt;
-&lt;p style=&quot; margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:large;&quot;&gt;album&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignCenter</set>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-    </widget>
-   </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <property name="spacing">
@@ -218,7 +99,7 @@ p, li { white-space: pre-wrap; }
       </spacer>
      </item>
      <item>
-      <widget class="QPushButton" name="prevButton">
+      <widget class="QPushButton" name="randomButton">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
          <horstretch>0</horstretch>
@@ -238,7 +119,10 @@ p, li { white-space: pre-wrap; }
         </size>
        </property>
        <property name="text">
-        <string>&lt;|</string>
+        <string>S</string>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
        </property>
        <property name="flat">
         <bool>true</bool>
@@ -246,7 +130,7 @@ p, li { white-space: pre-wrap; }
       </widget>
      </item>
      <item>
-      <widget class="QPushButton" name="playButton">
+      <widget class="QPushButton" name="repeatButton">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
          <horstretch>0</horstretch>
@@ -266,7 +150,10 @@ p, li { white-space: pre-wrap; }
         </size>
        </property>
        <property name="text">
-        <string>&gt;</string>
+        <string>R</string>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
        </property>
        <property name="flat">
         <bool>true</bool>
@@ -274,7 +161,20 @@ p, li { white-space: pre-wrap; }
       </widget>
      </item>
      <item>
-      <widget class="QPushButton" name="pauseButton">
+      <spacer name="horizontalSpacer_4">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="prevButton">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
          <horstretch>0</horstretch>
@@ -294,7 +194,35 @@ p, li { white-space: pre-wrap; }
         </size>
        </property>
        <property name="text">
-        <string>||</string>
+        <string>&lt;|</string>
+       </property>
+       <property name="flat">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="playpauseButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>70</width>
+         <height>70</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>70</width>
+         <height>70</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>&gt;||</string>
        </property>
        <property name="flat">
         <bool>true</bool>
@@ -359,6 +287,124 @@ p, li { white-space: pre-wrap; }
      </item>
     </layout>
    </item>
+   <item>
+    <widget class="QStackedWidget" name="stackedWidget">
+     <property name="currentIndex">
+      <number>1</number>
+     </property>
+     <widget class="QWidget" name="page">
+      <layout class="QVBoxLayout" name="verticalLayout_4">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QListView" name="playlistView">
+         <property name="contextMenuPolicy">
+          <enum>Qt::CustomContextMenu</enum>
+         </property>
+         <property name="editTriggers">
+          <set>QAbstractItemView::NoEditTriggers</set>
+         </property>
+         <property name="textElideMode">
+          <enum>Qt::ElideLeft</enum>
+         </property>
+         <property name="uniformItemSizes">
+          <bool>true</bool>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="page_2">
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="0" column="0">
+        <widget class="QWidget" name="widget" native="true">
+         <layout class="QVBoxLayout" name="verticalLayout_2">
+          <item>
+           <layout class="QHBoxLayout" name="horizontalLayout_5">
+            <item>
+             <spacer name="horizontalSpacer_2">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item>
+             <widget class="QLabel" name="countLabel">
+              <property name="text">
+               <string>0/0</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <widget class="QLabel" name="titleLabel">
+            <property name="text">
+             <string>&lt;h3&gt;Title&lt;/h3&gt;</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="progressLayout">
+            <item>
+             <widget class="QLabel" name="doneTimeLabel">
+              <property name="text">
+               <string>00:00</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="allTimeLabel">
+              <property name="text">
+               <string>00:00</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <widget class="QLabel" name="artistAlbumLabel">
+            <property name="text">
+             <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:large; font-weight:600;&quot;&gt;Artist&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:large;&quot;&gt;album&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+      <zorder></zorder>
+      <zorder>widget</zorder>
+     </widget>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>