/* * Copyright 2008, 2011 Jason A. Donenfeld */ #include "PlayerWindow.h" #include "Song.h" #include "CollectionModel.h" #include "CollectionFilter.h" #include "SearchLineEdit.h" #include "Credentials.h" #include "AutoSizingList.h" #include #include #include #include #include #include #include #include #include //BEGIN CONSTRUCT OBJECTS PlayerWindow::PlayerWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), m_model(0), m_filter(0) { setupUi(); setupPhonon(); } void PlayerWindow::setupUi() { m_playAction = new QAction(style()->standardIcon(QStyle::SP_MediaPlay), tr("Play"), this); m_pauseAction = new QAction(style()->standardIcon(QStyle::SP_MediaPause), tr("Pause"), this); m_stopAction = new QAction(style()->standardIcon(QStyle::SP_MediaStop), tr("Stop"), this); m_nextAction = new QAction(style()->standardIcon(QStyle::SP_MediaSkipForward), tr("Next"), this); m_previousAction = new QAction(style()->standardIcon(QStyle::SP_MediaSkipBackward), tr("Previous"), this); connect(m_playAction, SIGNAL(triggered()), this, SLOT(playTriggered())); connect(m_pauseAction, SIGNAL(triggered()), this, SLOT(pauseTriggered())); connect(m_stopAction, SIGNAL(triggered()), this, SLOT(stopTriggered())); connect(m_nextAction, SIGNAL(triggered()), this, SLOT(nextTriggered())); connect(m_previousAction, SIGNAL(triggered()), this, SLOT(previousTriggered())); m_playAction->setDisabled(true); m_pauseAction->setDisabled(true); m_stopAction->setDisabled(true); m_nextAction->setDisabled(true); m_previousAction->setDisabled(true); m_volumeSlider = new Phonon::VolumeSlider(this); m_volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); m_seekSlider = new Phonon::SeekSlider(this); m_seekSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); QToolBar *toolBar = new QToolBar(this); toolBar->addAction(m_previousAction); toolBar->addAction(m_playAction); //TODO: make it play/pause one button toolBar->addAction(m_pauseAction); toolBar->addAction(m_stopAction); toolBar->addAction(m_nextAction); toolBar->addWidget(m_seekSlider); toolBar->addWidget(m_volumeSlider); toolBar->setIconSize(QSize(16, 16)); toolBar->setMovable(false); setUnifiedTitleAndToolBarOnMac(true); addToolBar(toolBar); QWidget *centralWidget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); layout->setMargin(0); m_searchEdit = new SearchLineEdit(this); m_searchEdit->setDisabled(true); m_searchEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); connect(m_searchEdit, SIGNAL(textChanged(const QString&)), this, SLOT(searchChanged(const QString&))); layout->addWidget(m_searchEdit); m_searchTimer.setSingleShot(true); m_searchTimer.setInterval(300); connect(&m_searchTimer, SIGNAL(timeout()), this, SLOT(updateSearch())); m_treeView = new AutoSizingList(this); layout->addWidget(m_treeView); centralWidget->setLayout(layout); setCentralWidget(centralWidget); m_moveToCurrent = new QPushButton(this); m_moveToCurrent->setIcon(style()->standardIcon(QStyle::SP_CommandLink)); m_moveToCurrent->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J)); m_moveToCurrent->setToolTip(tr("Move to Currently Playing Song")); connect(m_moveToCurrent, SIGNAL(clicked()), this, SLOT(moveToCurrent())); statusBar()->addPermanentWidget(m_moveToCurrent); m_moveToCurrent->hide(); m_downloadProgress = new QProgressBar; statusBar()->addWidget(m_downloadProgress, 1); statusBar()->hide(); m_credentials = new Credentials("music.zx2c4.com", "getlisting.php?language=xml", "getsong.php?hash=%1"); connect(m_credentials, SIGNAL(receivedList(const QList)), this, SLOT(setupTable(const QList))); m_treeView->setChildWidget(m_credentials); m_credentials->setDownloadProgressBar(m_downloadProgress); m_credentials->setStatusBar(statusBar()); m_credentials->show(); setMinimumSize(500, 400); setWindowTitle("ZX2C4 Music Player"); setWindowIcon(style()->standardIcon(QStyle::SP_DriveCDIcon)); show(); } void PlayerWindow::setupPhonon() { m_mediaObject = new Phonon::MediaObject(this); m_mediaObject->setPrefinishMark(5000);//TODO: what should the real value be? m_aboutToFinishTimer.setSingleShot(true); connect(&m_aboutToFinishTimer, SIGNAL(timeout()), this, SLOT(songFinished())); connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(playerStateChanged(Phonon::State,Phonon::State))); connect(m_mediaObject, SIGNAL(prefinishMarkReached(qint32)), this, SLOT(aboutToFinish(qint32))); connect(m_mediaObject, SIGNAL(bufferStatus(int)), m_downloadProgress, SLOT(setValue(int))); Phonon::AudioOutput *audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, m_mediaObject); Phonon::createPath(m_mediaObject, audioOutput); m_volumeSlider->setAudioOutput(audioOutput); m_seekSlider->setMediaObject(m_mediaObject); } void PlayerWindow::setupTable(const QList songList) { m_model = new CollectionModel(songList, m_credentials, this); m_filter = new CollectionFilter(this, m_model); m_treeView->setModel(m_filter); m_searchEdit->setDisabled(false); connect(m_treeView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(songDoubleClicked(const QModelIndex&))); updateControls(); } //END CONSTRUCT OBJECTS //BEGIN SYNC PLAYER CONTROLS void PlayerWindow::playerStateChanged(Phonon::State /*newState*/, Phonon::State /*oldState*/) { updateControls(); } void PlayerWindow::updateControls() { QString message; Song *currentSong = m_model->song(m_filter->currentItem()); if(currentSong) message = QString("%4: %1 - %2 - %3").arg(currentSong->artist()).arg(currentSong->album()).arg(currentSong->title()); statusBar()->clearMessage(); switch(m_mediaObject->state()) { case Phonon::LoadingState: m_playAction->setDisabled(true); m_pauseAction->setDisabled(true); m_stopAction->setDisabled(true); m_downloadProgress->hide(); if(currentSong) statusBar()->showMessage(message.arg(tr("Loading"))); break; case Phonon::StoppedState: m_stopAction->setDisabled(true); m_pauseAction->setDisabled(true); m_playAction->setDisabled(!m_filter->currentItem().isValid()); m_downloadProgress->hide(); break; case Phonon::PausedState: m_playAction->setDisabled(false); m_pauseAction->setDisabled(true); m_downloadProgress->hide(); if(currentSong) statusBar()->showMessage(message.arg(tr("Paused"))); break; case Phonon::PlayingState: m_playAction->setDisabled(true); m_pauseAction->setDisabled(false); m_stopAction->setDisabled(false); m_downloadProgress->hide(); if(currentSong) statusBar()->showMessage(message.arg(tr("Playing"))); break; case Phonon::BufferingState: m_downloadProgress->setMaximum(100); m_downloadProgress->show(); break; case Phonon::ErrorState: m_downloadProgress->hide(); statusBar()->showMessage(m_mediaObject->errorString()); break; } m_previousAction->setDisabled(!m_filter->peekPrevious().isValid() || m_filter->rowCount() < 2); m_nextAction->setDisabled(!m_filter->peekNext().isValid() || m_filter->rowCount() < 2); } //END SYNC PLAYER CONTROLS //BEGIN CHANGE SONG void PlayerWindow::songDoubleClicked(const QModelIndex &index) { m_aboutToFinishTimer.stop(); m_filter->setCurrentItem(index); playIndex(m_filter->currentItem()); } void PlayerWindow::nextTriggered() { m_aboutToFinishTimer.stop(); playIndex(m_filter->moveNext()); } void PlayerWindow::previousTriggered() { m_aboutToFinishTimer.stop(); playIndex(m_filter->movePrevious()); } void PlayerWindow::pauseTriggered() { m_aboutToFinishTimer.stop(); m_mediaObject->pause(); } void PlayerWindow::playTriggered() { m_aboutToFinishTimer.stop(); m_mediaObject->play(); } void PlayerWindow::stopTriggered() { m_aboutToFinishTimer.stop(); m_mediaObject->stop(); } void PlayerWindow::playIndex(const QModelIndex &index) { if(index.isValid()) { m_mediaObject->setCurrentSource(m_credentials->songUrl(m_model->song(index)->sha1())); m_mediaObject->play(); updateControls(); m_moveToCurrent->show(); } } void PlayerWindow::aboutToFinish(qint32 msecToEnd) { m_aboutToFinishTimer.setInterval(msecToEnd); m_aboutToFinishTimer.start(); //TODO: instead enqueue song for pre-buffering for potential fades } void PlayerWindow::songFinished() { playIndex(m_filter->moveNext()); } //END CHANGE SONG //BEGIN FILTERING void PlayerWindow::searchChanged(const QString&) { m_searchTimer.stop(); m_searchTimer.start(); } void PlayerWindow::updateSearch() { if(m_filter) { m_filter->setFilter(m_searchEdit->text().trimmed()); updateControls(); } } void PlayerWindow::moveToCurrent() { if(m_filter) { QModelIndex index = m_filter->currentItem(); if(!m_filter->mapFromSource(index).isValid()) { m_searchEdit->clear(); updateSearch(); } m_treeView->scrollTo(m_filter->mapFromSource(index), QAbstractItemView::PositionAtCenter); } } //END FILTERING