#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "playerwidget.h" #include "beetview.h" #include "indexerdialog.h" #include "globals.h" #include "helper.h" PlayerWidget::PlayerWidget(QWidget *parent) : QWidget(parent), mDurSecs(0), mPlayListLength(0){ mStarting = true; setupGui(); createActions(); mStarting = false; } PlayerWidget::~PlayerWidget(){ writeSettings(); } void PlayerWidget::setupGui(){ //the Player mPlayer = new QMediaPlayer(this); connect(mPlayer, &QMediaPlayer::positionChanged, this, &PlayerWidget::setPosition); connect(mPlayer, &QMediaPlayer::mediaStatusChanged, this, &PlayerWidget::continuePlaying); //tray icon mTrayIcon = new QSystemTrayIcon(this); mTrayIcon->setIcon(QIcon(":/stop.png")); mTrayIcon->show(); mVolumeTimer = new QTimer(this); mVolumeTimer->setSingleShot(true); connect(mVolumeTimer, &QTimer::timeout, this, &PlayerWidget::showVolume); //THE view mView = new BeetView; mView->setAlternatingRowColors(true); mViewModel = new QStandardItemModel; mView->setModel(mViewModel); mSearchModel = new QStandardItemModel; mView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(mView, &BeetView::doubleClicked, this, &PlayerWidget::viewDoubleClicked); connect(mView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PlayerWidget::leftCurrentChanged); mFolderModel = new QStandardItemModel; mCurrentModel = mViewModel; QToolBar *viewTB = new QToolBar; QActionGroup *viewAG = new QActionGroup(this); viewAG->setExclusive(true); mViewByArtistA = new QAction(QIcon(":/artist.png"), tr("View by artist"), this); mViewByArtistA->setCheckable(true); viewAG->addAction(mViewByArtistA); connect(mViewByArtistA, &QAction::triggered, this, &PlayerWidget::doPopulateByArtist); QAction *viewByAlbumA = new QAction(QIcon(":/album.png"), tr("View by album"), this); viewByAlbumA->setCheckable(true); viewAG->addAction(viewByAlbumA); connect(viewByAlbumA, &QAction::triggered, this, &PlayerWidget::doPopulateByAlbum); QAction *viewBySongA = new QAction(QIcon(":/song.png"), tr("View by song"), this); viewBySongA->setCheckable(true); viewAG->addAction(viewBySongA); connect(viewBySongA, &QAction::triggered, this, &PlayerWidget::doPopulateBySong); QAction *viewByGenreA = new QAction(QIcon(":/genre.png"), tr("View by genre"), this); viewByGenreA->setCheckable(true); viewAG->addAction(viewByGenreA); connect(viewByGenreA, &QAction::triggered, this, &PlayerWidget::doPopulateByGenre); QAction *viewByDateA = new QAction(QIcon(":/sissyd.png"), tr("View by date"), this); viewByDateA->setCheckable(true); viewAG->addAction(viewByDateA); connect(viewByDateA, &QAction::triggered, this, &PlayerWidget::doPopulateByDate); viewAG->addAction(Helper::createSeparator(this)); QAction *viewByFolderA = new QAction(QIcon(":/folder.png"), tr("View by folder"), this); viewByFolderA->setCheckable(true); viewAG->addAction(viewByFolderA); connect(viewByFolderA, &QAction::triggered, this, &PlayerWidget::doPopulateByFolder); viewAG->addAction(Helper::createSeparator(this)); mSearchA = new QAction(QIcon(":/gaping_ass.png"), tr("View search"), this); mSearchA->setCheckable(true); viewAG->addAction(mSearchA); connect(mSearchA, &QAction::triggered, this, &PlayerWidget::doFilter); viewTB->addActions(viewAG->actions()); mSelectFilesA = new QAction(QIcon(":/bizarre_amputee.png"), tr("Select files..."), this); mSelectFilesA->setShortcut(tr("CTRL++")); connect(mSelectFilesA, &QAction::triggered, this, &PlayerWidget::doSelectFiles); mDeselectAllA = new QAction(QIcon(":/gaping_ass.png"), tr("Clear Selection"), this); mDeselectAllA->setShortcut(tr("CTRL+-")); connect(mDeselectAllA, &QAction::triggered, this, &PlayerWidget::doDeleteFiles); mDeleteFilesA = new QAction(QIcon(":/delete.png"), tr("Delete files..."), this); mDeleteFilesA->setShortcut(QKeySequence::Delete); connect(mDeleteFilesA, &QAction::triggered, this, &PlayerWidget::doDeleteFiles); mRefreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this); connect(mRefreshA, &QAction::triggered, this, &PlayerWidget::doPopulateByFolder); //filter QGroupBox *filterGB = new QGroupBox(tr("Search")); mSearch = new QLineEdit; connect(mSearch, &QLineEdit::returnPressed, this, &PlayerWidget::doFilter); QToolBar *searchTB = new QToolBar; QAction *clearSearchA = new QAction(QIcon(":/clean_tampon.png"), tr("Clear search"), this); connect(clearSearchA, &QAction::triggered, this, &PlayerWidget::clearFilter); searchTB->addAction(clearSearchA); QAction *doSearchA = new QAction(QIcon(":/stomp.png"), tr("Go searching!"), this); connect(doSearchA, &QAction::triggered, this, &PlayerWidget::doFilter); searchTB->addAction(doSearchA); QHBoxLayout *filterLayout = new QHBoxLayout; filterLayout->addWidget(mSearch); filterLayout->addWidget(searchTB); filterGB->setLayout(filterLayout); //directories QGroupBox *dirGB = new QGroupBox(QString(tr("Current Directory"))); mDir = new QLineEdit; mDir->setEnabled(false); QToolBar *dirTB = new QToolBar; QImage upImg(":/stomp.png"); upImg = upImg.mirrored(); QIcon upDirIcon = QPixmap::fromImage(upImg); QAction *upDirA = new QAction(upDirIcon, tr("Up directory"), this); connect(upDirA, &QAction::triggered, this, &PlayerWidget::dirUp); dirTB->addAction(upDirA); QAction *homeDirA = new QAction(QIcon(":/home.png"), tr("Go home"), this); connect(homeDirA, &QAction::triggered, this, &PlayerWidget::dirHome); dirTB->addAction(homeDirA); dirTB->addAction(mRefreshA); QHBoxLayout *dirLayout = new QHBoxLayout; dirLayout->addWidget(mDir); dirLayout->addWidget(dirTB); dirGB->setLayout(dirLayout); //stack it up! mSearchDirStack = new QStackedLayout; mSearchDirStack->addWidget(filterGB); mSearchDirStack->addWidget(dirGB); //left widget QWidget *leftWidget = new QWidget; QVBoxLayout *leftWidgetL = new QVBoxLayout; leftWidgetL->addLayout(mSearchDirStack); leftWidgetL->addWidget(mView, 999); QHBoxLayout *selViewL = new QHBoxLayout; selViewL->addWidget(new QLabel(tr("View by:"))); selViewL->addStretch(); selViewL->addWidget(viewTB); selViewL->addStretch(); leftWidgetL->addLayout(selViewL); leftWidget->setLayout(leftWidgetL); connect(this, &PlayerWidget::modelChanged, this, &PlayerWidget::doModelChanged); //now playing label mNowPlayingL = new QLabel; mNowPlayingL->setAlignment(Qt::AlignCenter); mNowPlayingL->setFont(QFont("courier new", 20, QFont::Bold)); mNowPlayingL->setText(tr("(none)")); //song slider QLabel *l1 = new QLabel(tr("Song")); mSongSlider = new QSlider; mSongSlider->setOrientation(Qt::Horizontal); connect(mSongSlider, &QSlider::sliderMoved, this, &PlayerWidget::slide); mPos = new QLabel(tr("00:00")); mPos->setFont(QFont("courier")); QHBoxLayout *songSliderL = new QHBoxLayout; songSliderL->addWidget(l1); songSliderL->addWidget(mSongSlider); songSliderL->addWidget(mPos); //now playing info QString currentInfoGBS = QString(tr("Now Playing %1")).arg(QChar(0x26A5)); QGroupBox *currentInfoGB = new QGroupBox(currentInfoGBS); mCurrentTE = new QTextEdit; mCurrentTE->setFont(QFont("courier")); mCurrentTE->setReadOnly(true); QVBoxLayout *currentInfoL = new QVBoxLayout; currentInfoL->addWidget(mCurrentTE); currentInfoGB->setLayout(currentInfoL); //current left info QString leftInfoGBS = QString(tr("%1 Selected")).arg(QChar(0x26A5)); QGroupBox *leftInfoGB = new QGroupBox(leftInfoGBS); mLeftTE = new QTextEdit; mLeftTE->setFont(QFont("courier")); mLeftTE->setReadOnly(true); QVBoxLayout *leftInfoL = new QVBoxLayout; leftInfoL->addWidget(mLeftTE); leftInfoGB->setLayout(leftInfoL); //current right info QString rightInfoGBS = QString(tr("Selected %1")).arg(QChar(0x26A5)); QGroupBox *rightInfoGB = new QGroupBox(rightInfoGBS); mRightTE = new QTextEdit; mRightTE->setFont(QFont("courier")); mRightTE->setReadOnly(true); QVBoxLayout *rightInfoL = new QVBoxLayout; rightInfoL->addWidget(mRightTE); rightInfoGB->setLayout(rightInfoL); //volume slider QLabel *l2 = new QLabel(tr("Volume")); mVolumeSlider = new QSlider; mVolumeSlider->setOrientation(Qt::Horizontal); mVolumeSlider->setMinimum(0); mVolumeSlider->setMaximum(100); mVolumePos = new QLabel(tr("000 %")); mVolumePos->setFont(QFont("courier")); connect(mVolumeSlider, &QSlider::valueChanged, mPlayer, &QMediaPlayer::setVolume); connect(mVolumeSlider, &QSlider::valueChanged, this, &PlayerWidget::volumeChanged); mVolumeSlider->setValue(33); QHBoxLayout *volumeL = new QHBoxLayout; volumeL->addWidget(l2); volumeL->addWidget(mVolumeSlider); volumeL->addWidget(mVolumePos); //center widget QWidget *centerWidget = new QWidget; QVBoxLayout *centerWidgetL = new QVBoxLayout; QSplitter *centerSplitter = new QSplitter; centerSplitter->setOrientation(Qt::Vertical); centerSplitter->addWidget(currentInfoGB); centerSplitter->addWidget(leftInfoGB); centerSplitter->addWidget(rightInfoGB); mToolBar = new QToolBar; centerWidgetL->addWidget(mToolBar); centerWidgetL->addWidget(mNowPlayingL); centerWidgetL->addLayout(songSliderL); centerWidgetL->addWidget(centerSplitter); centerWidgetL->addLayout(volumeL); centerWidget->setLayout(centerWidgetL); //playlist mPlayListModel = new QStandardItemModel; mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); mPlayListView = new BeetView; mPlayListView->setAlternatingRowColors(true); mPlayListView->setModel(mPlayListModel); mPlayListView->setRootIsDecorated(false); mPlayListView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(mPlayListView, &BeetView::doubleClicked, this, &PlayerWidget::playCurrent); connect(mPlayListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PlayerWidget::rightCurrentChanged); QGroupBox *playListGB = new QGroupBox(tr("Playlist")); QVBoxLayout *playListL = new QVBoxLayout; playListL->addWidget(mPlayListView); playListGB->setLayout(playListL); //right widget QWidget *rightWidget = new QWidget; QVBoxLayout *rightWidgetL = new QVBoxLayout; rightWidgetL->addWidget(playListGB); rightWidget->setLayout(rightWidgetL); //put it all together QSplitter *splitter = new QSplitter; splitter->addWidget(leftWidget); splitter->addWidget(centerWidget); splitter->addWidget(rightWidget); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(splitter); setLayout(mainLayout); } void PlayerWidget::createActions(){ QActionGroup *playAG = new QActionGroup(this); playAG->setExclusive(true); mPlayA = new QAction(QIcon(":/play.png"), tr("Play"), this); mPlayA->setCheckable(true); playAG->addAction(mPlayA); connect(mPlayA, &QAction::triggered, this, &PlayerWidget::doPlay); mPauseA = new QAction(QIcon(":/pause.png"), tr("Pause"), this); mPauseA->setCheckable(true); playAG->addAction(mPauseA); connect(mPauseA, &QAction::triggered, this, &PlayerWidget::doPause); mStopA = new QAction(QIcon(":/stop.png"), tr("Stop"), this); mStopA->setCheckable(true); playAG->addAction(mStopA); mStopA->setChecked(true); connect(mStopA, &QAction::triggered, this, &PlayerWidget::doStop); QAction *playOrPauseA = new QAction(tr("Play or Pause"), this); playOrPauseA->setObjectName("beetPlayerDoPlayOrPause"); connect(playOrPauseA, &QAction::triggered, this, &PlayerWidget::doPlayOrPause); KGlobalAccel::self()->setShortcut(playOrPauseA, QList() << QKeySequence(Qt::Key_MediaPlay), KGlobalAccel::Autoloading); QAction *previousA = new QAction(QIcon(":/previous.png"), tr("Previous"), this); previousA->setObjectName("beetPlayerPrevious"); connect(previousA, &QAction::triggered, this, &PlayerWidget::previous); KGlobalAccel::self()->setShortcut(previousA, QList() << QKeySequence(Qt::Key_Launch2), KGlobalAccel::Autoloading); QAction *nextA = new QAction(QIcon(":/next.png"), tr("Next"), this); nextA->setObjectName("beetPlayerNext"); connect(nextA, &QAction::triggered, this, &PlayerWidget::next); KGlobalAccel::self()->setShortcut(nextA, QList() << QKeySequence(Qt::Key_Launch3), KGlobalAccel::Autoloading); QAction *addToPlayListA = new QAction(QIcon(":/belly_right.png"), tr("Add to playlist"), this); connect(addToPlayListA, &QAction::triggered, this, &PlayerWidget::addToPlayList); QAction *addToPlayListAndClearA = new QAction(QIcon(":/belly_right_and_clear.png"), tr("Clear and add"), this); connect(addToPlayListAndClearA, &QAction::triggered, this, &PlayerWidget::addToPlayListAndClear); QAction *expandA = new QAction(Helper::iconFromQChar(QChar(0x2640), 90), tr("Expand"), this); connect(expandA, &QAction::triggered, this, &PlayerWidget::expand); QAction *collapseAllA = new QAction(Helper::iconFromQChar(QChar(0x2642), 120), tr("Collapse all"), this); connect(collapseAllA, &QAction::triggered, mView, &BeetView::collapseAll); QAction *removeFromPlayListA = new QAction(QIcon(":/belly_left.png"), tr("Remove from playlist"), this); connect(removeFromPlayListA, &QAction::triggered, this, &PlayerWidget::removeFromPlayList); QAction *clearPlayListA = new QAction(QIcon(":/delete.png"), tr("Clear Playlist"), this); connect(clearPlayListA, &QAction::triggered, this, &PlayerWidget::clearPlayList); QAction *refreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh..."), this); connect(refreshA, &QAction::triggered, this, &PlayerWidget::reindex); QAction *shufflePlayistA = new QAction(QIcon(":/shuffle.png"), tr("Shuffle playlist"), this); connect(shufflePlayistA, &QAction::triggered, this, &PlayerWidget::shufflePlayList); QAction *randomPlayA = new QAction(QIcon(":/dice.png"), tr("Play random"), this); connect(randomPlayA, &QAction::triggered, this, &PlayerWidget::randomPlay); QAction *muteA = new QAction(QIcon(":/mute.png"), tr("Mute"), this); muteA->setObjectName("beetPlayerMute"); muteA->setCheckable(true); connect(muteA, &QAction::triggered, this, &PlayerWidget::mute); KGlobalAccel::self()->setShortcut(muteA, QList() << QKeySequence(Qt::Key_VolumeMute), KGlobalAccel::Autoloading); QAction *volumeUpA = new QAction(tr("Increase volume"), this); volumeUpA->setObjectName("beetPlayerIncVolume"); connect(volumeUpA, &QAction::triggered, this, &PlayerWidget::volumeUp); KGlobalAccel::self()->setShortcut(volumeUpA, QList() << QKeySequence(Qt::Key_VolumeUp), KGlobalAccel::Autoloading); QAction *volumeDownA = new QAction(tr("Decrease volume"), this); volumeDownA->setObjectName("beetPlayerDecVolume"); connect(volumeDownA, &QAction::triggered, this, &PlayerWidget::volumeDown); KGlobalAccel::self()->setShortcut(volumeDownA, QList() << QKeySequence(Qt::Key_VolumeDown), KGlobalAccel::Autoloading); QAction *configA = Globals::instance()->action(Globals::ConfigAction); QAction *helpA = new QAction(Helper::iconFromQChar(QChar(0x00BF), 50), tr("Help"), this); QMenu *helpM = new QMenu; QAction *aboutThisA = new QAction(tr("About..."), this); connect(aboutThisA, &QAction::triggered, this, &PlayerWidget::aboutDlg);; helpM->addAction(aboutThisA); QAction *aboutQtA = new QAction(tr("About Qt..."), this); connect(aboutQtA, &QAction::triggered, qApp, &QApplication::aboutQt); helpM->addAction(aboutQtA); helpA->setMenu(helpM); mView->addAction(addToPlayListA); mView->addAction(addToPlayListAndClearA); mView->addAction(Helper::createSeparator(this)); mView->addAction(expandA); mView->addAction(collapseAllA); mView->addAction(Helper::createSeparator(this)); mView->addAction(mSelectFilesA); mView->addAction(mDeselectAllA); mView->addAction(mDeleteFilesA); mView->addAction(Helper::createSeparator(this)); mView->addAction(mRefreshA); mView->addAction(Helper::createSeparator(this)); mView->addAction(randomPlayA); mPlayListView->addAction(removeFromPlayListA); mPlayListView->addAction(shufflePlayistA); mPlayListView->addAction(clearPlayListA); QWidget* spacer1 = new QWidget(); spacer1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mToolBar->addWidget(spacer1); mToolBar->addAction(mPlayA); mToolBar->addAction(mPauseA); mToolBar->addAction(mStopA); mToolBar->addSeparator(); mToolBar->addAction(previousA); mToolBar->addAction(nextA); mToolBar->addSeparator(); mToolBar->addAction(addToPlayListA); mToolBar->addAction(removeFromPlayListA); mToolBar->addAction(clearPlayListA); mToolBar->addAction(shufflePlayistA); mToolBar->addAction(randomPlayA); mToolBar->addSeparator(); mToolBar->addAction(refreshA); mToolBar->addSeparator(); mToolBar->addAction(muteA); mToolBar->addSeparator(); mToolBar->addAction(configA); mToolBar->addSeparator(); mToolBar->addAction(helpA); QWidget* spacer2 = new QWidget(); spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mToolBar->addWidget(spacer2); QMenu *trayMenu = new QMenu; trayMenu->addAction(mPlayA); trayMenu->addAction(mStopA); trayMenu->addAction(mPauseA); trayMenu->addSeparator(); trayMenu->addAction(nextA); trayMenu->addAction(previousA); trayMenu->addSeparator(); QAction *quitA = new QAction(tr("Quit"), this); connect(quitA, &QAction::triggered, qApp, &QApplication::quit); trayMenu->addAction(quitA); mTrayIcon->setContextMenu(trayMenu); } void PlayerWidget::populateByArtist(QStandardItem *parent, const QString &filter){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QSqlQuery artistsQ(db); if(!filter.isEmpty()){ artistsQ.prepare("SELECT iartists_id, tartists_name FROM artists WHERE tartists_name ~ :f ORDER BY tartists_name ASC"); artistsQ.bindValue(":f", filter); }else{ artistsQ.prepare("SELECT iartists_id, tartists_name FROM artists ORDER BY tartists_name ASC"); } QSqlQuery albumQ(db); albumQ.prepare("SELECT DISTINCT(songs.ialbums_id), talbum_name, siyear FROM songs, albums WHERE songs.iartists_id = :artistid AND songs.ialbums_id = albums.ialbums_id ORDER BY siyear ASC"); QSqlQuery songQ(db); songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, ilength FROM songs WHERE ialbums_id = :alid AND iartists_id = :arid ORDER BY sipos ASC"); QIcon songIcon(":/song.png"); QIcon albumIcon(":/album.png"); QIcon artistIcon(":/artist.png"); //read data artistsQ.exec(); while(artistsQ.next()){ QStandardItem *curArtist = new QStandardItem; curArtist->setEditable(false); curArtist->setFont(QFont("courier")); curArtist->setText(artistsQ.value(1).toString()); curArtist->setIcon(artistIcon); curArtist->setData(Artist, TypeRole); curArtist->setData(artistsQ.value(0).toInt(), IdRole); root->appendRow(curArtist); albumQ.bindValue(":artistid", artistsQ.value(0)); albumQ.exec(); while(albumQ.next()){ QStandardItem *curAlbum = new QStandardItem; curAlbum->setEditable(false); curAlbum->setFont(QFont("courier")); QString albumText = QString(tr("%1 - %2")).arg(QString::number(albumQ.value(2).toInt())).arg(albumQ.value(1).toString()); curAlbum->setText(albumText); curAlbum->setIcon(albumIcon); curAlbum->setData(Album, TypeRole); curAlbum->setData(albumQ.value(0), IdRole); curArtist->appendRow(curAlbum); songQ.bindValue(":alid", albumQ.value(0)); songQ.bindValue(":arid", artistsQ.value(0)); songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 - %2")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(artistsQ.value(1), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(4), LengthRole); curSong->setData(albumQ.value(1), AlbumRole); curAlbum->appendRow(curSong); } } } } void PlayerWidget::populateByAlbum(QStandardItem *parent, const QVariant &filter, int type){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QIcon albumIcon(":/album.png"); QIcon songIcon(":/song.png"); QSqlQuery albumQ(db); if(type == EmptyType){ albumQ.prepare("SELECT DISTINCT(ialbums_id), talbum_name, siyear FROM albums ORDER BY talbum_name"); }else if(type == FilterType){ albumQ.prepare("SELECT DISTINCT(songs.ialbums_id), talbum_name, siyear FROM songs, albums WHERE talbum_name ~ :album AND songs.ialbums_id = albums.ialbums_id ORDER BY siyear ASC"); albumQ.bindValue(":album", filter); } albumQ.exec(); while(albumQ.next()){ QHash artistcount; QStandardItem *curAlbum = new QStandardItem; curAlbum->setEditable(false); curAlbum->setFont(QFont("courier")); curAlbum->setIcon(albumIcon); curAlbum->setData(Album, TypeRole); curAlbum->setData(albumQ.value(0), IdRole); parent->appendRow(curAlbum); QSqlQuery songQ = QSqlQuery(db); songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE albums.ialbums_id = :id AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY sipos"); songQ.bindValue(":id", albumQ.value(0)); songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 - %2 - %3")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(songQ.value(4), ArtistRole); ++artistcount[songQ.value(4).toString()]; curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); curSong->setData(songQ.value(6), LengthRole); curAlbum->appendRow(curSong); } QString albumText; if(artistcount.keys().count() > 1){ albumText = QString(tr("%1 - VA - (%2)")).arg(albumQ.value(1).toString()).arg(QString::number(albumQ.value(2).toInt())); }else{ albumText = QString(tr("%1 - %2 - (%3)")).arg(albumQ.value(1).toString()).arg(artistcount.keys().first()). arg(QString::number(albumQ.value(2).toInt())); } curAlbum->setText(albumText); } } void PlayerWidget::populateBySong(QStandardItem *parent, const QVariant &filter, int type){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QIcon songIcon(":/song.png"); QSqlQuery songQ = QSqlQuery(db); if(type == EmptyType){ songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); }else if(type == FilterType){ songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE ttitle ~ :f AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); songQ.bindValue(":f", filter); }else if(type == IdType){ songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE albums.ialbums_id = :id AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY sipos"); songQ.bindValue(":id", filter); } songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText; if(type == IdType){ songText = QString(tr("%1 - %2")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()); }else{ songText = QString(tr("%1 (%2)")).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); } curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(songQ.value(4), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); curSong->setData(songQ.value(6), LengthRole); root->appendRow(curSong); } } void PlayerWidget::populateByDate(QStandardItem *parent){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QIcon dateIcon(":/bizarre_amputee.png"); root->setIcon(dateIcon); QIcon albumIcon(":/album.png"); QIcon songIcon(":/song.png"); QSqlQuery q1("SELECT DISTINCT(albums.ialbums_id), talbum_name, dadded, tartists_name FROM albums, artists, songs WHERE albums.ialbums_id = songs.ialbums_id AND songs.iartists_id = artists.iartists_id ORDER BY dadded DESC", db); QSqlQuery songQ(db); songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE albums.ialbums_id = :id AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY sipos"); QHash years; QHash yearMonths; while(q1.next()){ QDate added = q1.value(2).toDate(); int year = added.year(); QStandardItem *yearItem = nullptr; QStandardItem *ymItem = nullptr; if(years.contains(year)){ yearItem = years.value(year); }else{ yearItem = new QStandardItem; yearItem->setText(QString::number(year)); years.insert(year, yearItem); yearItem->setIcon(dateIcon); root->appendRow(yearItem); } QString yearMonth = QString("%1-%2").arg(QString::number(added.year())).arg(added.month(), 2, 10, QChar('0')); if(yearMonths.contains(yearMonth)){ ymItem = yearMonths.value(yearMonth); }else{ ymItem = new QStandardItem; ymItem->setText(yearMonth); ymItem->setIcon(dateIcon); yearMonths.insert(yearMonth, ymItem); yearItem->appendRow(ymItem); } QStandardItem *albumItem = new QStandardItem; QString albumText = QString("%1 - %2").arg(q1.value(3).toString()).arg(q1.value(1).toString()); albumItem->setText(albumText); albumItem->setIcon(albumIcon); ymItem->appendRow(albumItem); songQ.bindValue(":id", q1.value(0)); songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 - %2 - %3")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(songQ.value(4), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); curSong->setData(songQ.value(6), LengthRole); albumItem->appendRow(curSong); } } } void PlayerWidget::populateByGenre(QStandardItem *parent, const QString &filter){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QIcon songIcon(":/song.png"); QIcon genreIcon(":/genre.png"); QSqlQuery genreQ(db); if(filter.isEmpty()){ genreQ.prepare("SELECT igenres_id, tgenres_name FROM genres ORDER BY tgenres_name"); }else{ genreQ.prepare("SELECT igenres_id, tgenres_name FROM genres WHERE tgenres_name ~ :f"); genreQ.bindValue(":f", filter); } genreQ.exec(); while(genreQ.next()){ QStandardItem *curGenre = new QStandardItem; curGenre->setEditable(false); curGenre->setFont(QFont("courier")); curGenre->setText(genreQ.value(1).toString()); curGenre->setIcon(genreIcon); curGenre->setData(Genre, TypeRole); curGenre->setData(genreQ.value(0), IdRole); root->appendRow(curGenre); QSqlQuery songQ = QSqlQuery(db); songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE igenres_id = :id AND songs.iartists_id = artists.iartists_id AND songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); songQ.bindValue(":id", genreQ.value(0)); songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 (%2)")).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(songQ.value(4), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); curSong->setData(songQ.value(6), LengthRole); curGenre->appendRow(curSong); } } } void PlayerWidget::doPopulateByFolder(){ mCurrentModel = mFolderModel; mFolderModel->clear(); mFolderModel->setHorizontalHeaderLabels(QStringList() << tr("Name")); QDir d(mCurDir); if(!d.exists()){ d = QDir(QDir::homePath()); mCurDir = d.absolutePath(); } QMimeDatabase db; QStandardItem *root = mFolderModel->invisibleRootItem(); QIcon songIcon(":/song.png"); QIcon otherIcon(":/belly_right_and_clear.png"); QIcon dirIcon(":/folder.png"); QFileInfoList fl = d.entryInfoList(QStringList() << "*", QDir::Files | QDir::Dirs | QDir::NoDot, QDir::Name | QDir::DirsFirst); foreach(QFileInfo fi, fl){ QStandardItem *cur = new QStandardItem; cur->setEditable(false); cur->setText(fi.fileName()); if(fi.isDir()){ cur->setIcon(dirIcon); }else if(fi.isFile()){ QMimeType mt = db.mimeTypeForFile(fi); if(mt.name().startsWith("audio")){ cur->setIcon(songIcon); cur->setData(Song, TypeRole); cur->setData(-1, IdRole); TagLib::FileRef file(fi.absoluteFilePath().toUtf8()); if(!file.isNull()){ cur->setData(QString::fromStdWString(file.tag()->artist().toWString()), ArtistRole); cur->setData(QString::fromStdWString(file.tag()->album().toWString()), AlbumRole); cur->setData(QString::fromStdWString(file.tag()->genre().toWString()), GenreRole); cur->setData(QString::fromStdWString(file.tag()->title().toWString()), TitleRole); } }else{ cur->setIcon(otherIcon); } } cur->setData(fi.absoluteFilePath(), FullPathRole); root->appendRow(cur); } mView->setModel(mFolderModel); emit viewModeChanged(tr("Folder")); emit modelChanged(); } void PlayerWidget::doModelChanged(){ if(mCurrentModel == mFolderModel){ mSelectFilesA->setEnabled(true); mDeselectAllA->setEnabled(true); mDeleteFilesA->setEnabled(true); mRefreshA->setEnabled(true); mSearchDirStack->setCurrentIndex(1); }else{ mSelectFilesA->setEnabled(false); mDeselectAllA->setEnabled(false); mDeleteFilesA->setEnabled(false); mRefreshA->setEnabled(false); mSearchDirStack->setCurrentIndex(0); } } void PlayerWidget::viewDoubleClicked(const QModelIndex &idx){ const QStandardItemModel *model = static_cast(idx.model()); if(model == mViewModel || model == mSearchModel){ addToPlayList(); return; } QString fp(idx.data(FullPathRole).toString()); QFileInfo fi(fp); if(fi.isDir()){ mCurDir = fp; mDir->setText(fp); doPopulateByFolder(); }else{ addToPlayList(); } } void PlayerWidget::leftCurrentChanged(const QModelIndex &cur, const QModelIndex &prev){ Q_UNUSED(prev) QString fullPath(cur.data(FullPathRole).toString()); TagLib::FileRef file(QString(fullPath).toUtf8()); fillWithText(mLeftTE, file); } void PlayerWidget::rightCurrentChanged(const QModelIndex &cur, const QModelIndex &prev){ Q_UNUSED(prev) QString fullPath(cur.data(FullPathRole).toString()); TagLib::FileRef file(QString(fullPath).toUtf8()); fillWithText(mRightTE, file); } void PlayerWidget::doPlay(){ if(mPlayer->state() == QMediaPlayer::PausedState){ mPlayer->play(); mPlayA->setChecked(true); emit playModeChanged(tr("Playing")); emit setWinTitle(mCurWinTitle); mTrayIcon->setIcon(QIcon(":/play.png")); mTrayIcon->setToolTip(mCurToolTip); return; } int playListCount = mPlayListModel->rowCount(); if(playListCount == 0){ emit message(tr("Playlist is empty! Dazed and confused, but trying to continue...")); doStop(); return; } QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); if(sel.isEmpty()){ mPlayListView->selectionModel()->select(mPlayListModel->index(0, 0), QItemSelectionModel::SelectCurrent); sel = mPlayListView->selectionModel()->selectedRows(); } playCurrent(sel.first()); } void PlayerWidget::doStop(){ mPlayer->stop(); mStopA->setChecked(true); emit playModeChanged(tr("Stopped")); QString winTitle = QString(tr("%1 [Stopped]")).arg(qApp->applicationName()); emit setWinTitle(winTitle); mTrayIcon->setIcon(QIcon(":/stop.png")); mTrayIcon->setToolTip(tr("[Stopped]")); } void PlayerWidget::doPause(){ mPlayer->pause(); mPauseA->setChecked(true); emit playModeChanged(tr("Paused")); QString winTitle = QString(tr("%1 [Paused]")).arg(qApp->applicationName()); emit setWinTitle(winTitle); mTrayIcon->setIcon(QIcon(":/pause.png")); mTrayIcon->setToolTip(tr("[Paused]")); } void PlayerWidget::doPlayOrPause(){ if(mPlayA->isChecked()){ doPause(); }else if(mPauseA->isChecked() || mStopA->isChecked()){ doPlay(); } } void PlayerWidget::doSelectFiles(){ bool ok; QString pattern = QInputDialog::getText(this, tr("Select files..."), tr("Pattern:"), QLineEdit::Normal, QString(), &ok); if(ok && !pattern.isEmpty()){ QList items = mFolderModel->findItems(pattern, Qt::MatchWildcard); mView->selectionModel()->clear(); foreach(QStandardItem *i, items){ QModelIndex idx = mFolderModel->indexFromItem(i); mView->selectionModel()->select(idx, QItemSelectionModel::Rows | QItemSelectionModel::Toggle); } } } void PlayerWidget::doDeselect(){ mView->selectionModel()->clearSelection(); } void PlayerWidget::doDeleteFiles(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); if(!sel.isEmpty()){ QString msg = QString(tr("Really delete %1 file(s)?")).arg(QString::number(sel.count())); int r = QMessageBox::question(this, tr("Delete files..."), msg); if(r == QMessageBox::Yes){ foreach(QModelIndex i, sel){ QString cur = i.data(FullPathRole).toString(); QFileInfo file(cur); if(file.isFile()){ QFile::remove(cur); continue; } if(file.isDir()){ QString d = file.absoluteFilePath(); QDir toDel(d); toDel.removeRecursively(); } } doPopulateByFolder(); } } } void PlayerWidget::volumeUp(){ adjustVolume(2); } void PlayerWidget::volumeDown(){ adjustVolume(-2); } void PlayerWidget::dirUp(){ QDir d(mCurDir); d.cdUp(); mDir->setText(d.absolutePath()); mCurDir = d.absolutePath(); doPopulateByFolder(); } void PlayerWidget::dirHome(){ mCurDir = QDir::homePath(); mDir->setText(mCurDir); doPopulateByFolder(); } void PlayerWidget::showVolume(){ int volume = mVolumeSlider->value(); QString msg = QString(tr("Volume: %1%")).arg(volume, 3, 10, QChar('0')); mTrayIcon->showMessage(msg, QString(), QSystemTrayIcon::Information, 2000); } void PlayerWidget::adjustVolume(int by){ int curVol = mVolumeSlider->value(); int newVol = curVol + by; if(newVol > 100){ newVol = 100; } if(newVol < 0){ newVol = 0; } mVolumeSlider->setValue(newVol); } void PlayerWidget::fillWithText(QTextEdit *te, const TagLib::FileRef &fr){ if(fr.isNull()){ return; } QString artist = QString::fromStdWString(fr.tag()->artist().toWString()); QString album = QString::fromStdWString(fr.tag()->album().toWString()); QString title = QString::fromStdWString(fr.tag()->title().toWString()); QString genre = QString::fromStdWString(fr.tag()->genre().toWString()); quint16 track = fr.tag()->track(); quint16 year = fr.tag()->year(); te->clear(); te->append(QString("%1 %2").arg(tr("Artist:"), -20).arg(artist)); te->append(QString("%1 %2").arg(tr("Album:"), -20).arg(album)); te->append(QString("%1 %2").arg(tr("Title:"), -20).arg(title)); te->append(QString("%1 %2").arg(tr("Genre:"), -20).arg(genre)); te->append(QString("%1 %2").arg(tr("Track:"), -20).arg(track, 3, 10, QChar('0'))); te->append(QString("%1 %2").arg(tr("Year:"), -20).arg(year, 4, 10)); te->append(QString("%1 %2 kb/s").arg(tr("Bitrate:"), -20).arg(fr.audioProperties()->bitrate(), 4, 10, QChar('0'))); } void PlayerWidget::recurse(const QModelIndex &parent){ for(int i = 0; i < mCurrentModel->rowCount(parent); ++i){ QModelIndex cur = mCurrentModel->index(i, 0, parent); int type = cur.data(TypeRole).toInt(); if(type == Song){ addSong(cur); }else{ recurse(cur); } } } void PlayerWidget::addSong(const QModelIndex &idx){ QStandardItem *root = mPlayListModel->invisibleRootItem(); QString title = idx.data(TitleRole).toString(); QString artist = idx.data(ArtistRole).toString(); QString album = idx.data(AlbumRole).toString(); QStringList args = QStringList() << title << artist << album; foreach(QString s, args){ if(s.isEmpty()){ s = QString(tr("n/a")); } } QString display = QString(tr("%1 - %2 - %3")).arg(artist).arg(title).arg(album); QStandardItem *item = new QStandardItem; item->setEditable(false); item->setFont(QFont("courier")); item->setText(display); item->setIcon(QIcon(":/song.png")); item->setData(idx.data(FullPathRole), FullPathRole); int len = idx.data(LengthRole).toInt(); if(len == 0){ len = Helper::lengthInSeconds(idx.data(FullPathRole).toString()); } item->setData(len, LengthRole); root->appendRow(item); mPlayListLength += len; } void PlayerWidget::doPopulateByArtist(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Artist name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by artist... Please wait!"))); qApp->processEvents(); populateByArtist(root, QString()); qApp->restoreOverrideCursor(); mViewByArtistA->setChecked(true); emit viewModeChanged(tr("Artist")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateByAlbum(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Album name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by album... Please wait!"))); qApp->processEvents(); populateByAlbum(root, QString(), EmptyType); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Album")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateByGenre(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Genre name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by genre... Please wait!"))); qApp->processEvents(); populateByGenre(root, QString()); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Genre")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateBySong(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Genre name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by song... Please wait!"))); qApp->processEvents(); populateBySong(root, QString(), EmptyType); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Song")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateByDate(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Date added")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by date... Please wait!"))); qApp->processEvents(); populateByDate(root); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Date")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doFilter(){ mSearchDirStack->setCurrentIndex(0); QString filter = mSearch->text(); mSearchModel->clear(); mSearchModel->setHorizontalHeaderLabels(QStringList() << tr("Name")); mView->setModel(mSearchModel); mCurrentModel = mSearchModel; emit viewModeChanged(tr("Search")); mSearchA->setChecked(true); if(filter.isEmpty()){ emit message(QString(tr("No search term set! Cowardly refusing to do anything!"))); return; } QString msg = QString(tr("Searching for \"%1\"")).arg(filter); emit message(msg); qApp->setOverrideCursor(Qt::BusyCursor); QStandardItem *root = mSearchModel->invisibleRootItem(); QStandardItem *artistI = new QStandardItem(QIcon(":/quadd.png"), tr("Artists")); artistI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(artistI); populateByArtist(artistI, filter); QStandardItem *albumI = new QStandardItem(QIcon(":/quadd.png"), tr("Albums")); albumI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(albumI); populateByAlbum(albumI, filter, FilterType); QStandardItem *genreI = new QStandardItem(QIcon(":/quadd.png"), tr("Genres")); genreI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(genreI); populateByGenre(genreI, filter); QStandardItem *songI = new QStandardItem(QIcon(":/quadd.png"), tr("Songs")); songI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(songI); populateBySong(songI, filter, FilterType); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Search")); emit modelChanged(); } void PlayerWidget::clearFilter(){ mSearch->clear(); mView->setModel(mViewModel); mCurrentModel = mViewModel; } void PlayerWidget::reindex(){ IndexerDialog dlg(this); dlg.exec(); } void PlayerWidget::addToPlayList(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); if(sel.isEmpty()){ return; } foreach(QModelIndex i, sel){ int type = i.data(TypeRole).toInt(); if(type == Song){ addSong(i); }else{ recurse(i); } } QStandardItem *playListRoot = mPlayListModel->invisibleRootItem(); emit numFilesChanged(playListRoot->rowCount()); emit playListLengthChanged(mPlayListLength); } void PlayerWidget::addToPlayListAndClear(){ clearPlayList(); addToPlayList(); } void PlayerWidget::removeFromPlayList(){ QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); QList persistent; int subSecs = 0; foreach(QModelIndex i, sel){ subSecs = i.data(LengthRole).toInt(); persistent << QPersistentModelIndex(i); } foreach(QPersistentModelIndex i, persistent){ mPlayListModel->removeRow(i.row()); } QStandardItem *root = mPlayListModel->invisibleRootItem(); emit numFilesChanged(root->rowCount()); mPlayListLength -= subSecs; emit playListLengthChanged(mPlayListLength); } void PlayerWidget::clearPlayList(){ mPlayListModel->clear(); mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); mPlayListLength = 0; emit numFilesChanged(0); emit playListLengthChanged(0); } void PlayerWidget::shufflePlayList(){ QVector items; for(int i = 0; i < mPlayListModel->rowCount(); ++i){ QStandardItem *cur = mPlayListModel->item(i, 0)->clone(); items << cur; } std::random_shuffle(items.begin(), items.end()); mPlayListModel->clear(); mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); QStandardItem *root = mPlayListModel->invisibleRootItem(); foreach(QStandardItem *i, items){ root->appendRow(i); } } void PlayerWidget::randomPlay(){ clearPlayList(); QStandardItem *root = mPlayListModel->invisibleRootItem(); QIcon songIcon(":/song.png"); QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QSqlQuery randomQ(db); randomQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE songs.iartists_id = artists.iartists_id AND songs.ialbums_id = albums.ialbums_id ORDER BY random() LIMIT 1000"); randomQ.exec(); while(randomQ.next()){ QString display = QString(tr("%1 - %2 - %3")).arg(randomQ.value(4).toString()).arg(randomQ.value(1).toString()).arg(randomQ.value(5).toString()); QStandardItem *item = new QStandardItem; item->setEditable(false); item->setFont(QFont("courier")); item->setText(display); item->setIcon(songIcon); item->setData(randomQ.value(2), FullPathRole); item->setData(randomQ.value(6), LengthRole); mPlayListLength += randomQ.value(6).toInt(); root->appendRow(item); } emit numFilesChanged(root->rowCount()); emit playListLengthChanged(mPlayListLength); } void PlayerWidget::playCurrent(const QModelIndex &index){ mPlayer->stop(); QString fullPath = index.data(FullPathRole).toString(); play(fullPath); mTrayIcon->setIcon(QIcon(":/play.png")); mTrayIcon->setToolTip(mCurToolTip); } void PlayerWidget::play(const QString &fullPath){ mPlayer->setMedia(QUrl::fromLocalFile(fullPath)); TagLib::FileRef file(QString(fullPath).toUtf8()); fillWithText(mCurrentTE, file); if(file.isNull()){ return; } QString artist = QString::fromStdWString(file.tag()->artist().toWString()); QString album = QString::fromStdWString(file.tag()->album().toWString()); QString title = QString::fromStdWString(file.tag()->title().toWString()); mNowPlayingL->setText(title); int length = file.audioProperties()->lengthInSeconds(); mDurSecs = length; mSongSlider->setMinimum(0); mSongSlider->setMaximum(mDurSecs); int minutes = mDurSecs / 60; int seconds = mDurSecs % 60; mCurrentTE->append(QString("%1 %2:%3").arg(tr("Length:"), -20).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'))); QString msg = QString("File: %1").arg(fullPath); emit message(msg); mCurWinTitle = QString(tr("%1 - [%2] - [%3] - [%4]")).arg(qApp->applicationName()).arg(artist).arg(album).arg(title); mCurToolTip = QString(tr("%1: [%2] - [%3]")).arg(artist).arg(album).arg(title); mTrayIcon->setToolTip(mCurToolTip); mTrayIcon->showMessage(QString(tr("Now Playing:")), mCurToolTip, QSystemTrayIcon::Information, 5000); emit setWinTitle(mCurWinTitle); mPlayer->play(); mPlayA->setChecked(true); emit playModeChanged(tr("Playing")); } void PlayerWidget::volumeChanged(int volume){ QString s = QString("%1 %").arg(volume, 3, 10, QChar('0')); mVolumePos->setText(s); if(!mStarting){ if(!mVolumeTimer->isActive()){ mVolumeTimer->start(500); } } } void PlayerWidget::mute(bool triggered){ mPlayer->setMuted(triggered); } void PlayerWidget::next(){ advance(1); } void PlayerWidget::previous(){ advance(-1); } void PlayerWidget::advance(int numSongs){ mPlayer->stop(); QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); if(sel.isEmpty()){ QStandardItem *root = mPlayListModel->invisibleRootItem(); if(root->rowCount() > 0){ QModelIndex first = mPlayListModel->index(0, 0); mPlayListView->selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect); playCurrent(first); return; } return; } QModelIndex cur = sel.first(); QModelIndex nextIdx = mPlayListModel->index(cur.row() + numSongs, 0); if(nextIdx.isValid()){ mPlayListView->selectionModel()->setCurrentIndex(nextIdx, QItemSelectionModel::ClearAndSelect); playCurrent(nextIdx); } } void PlayerWidget::slide(int value){ qint64 newValue = value * 1000; mPlayer->setPosition(newValue); } void PlayerWidget::setPosition(qint64 pos){ int curPos = pos / 1000; mSongSlider->setValue(curPos); int minutes = curPos / 60; int seconds = curPos % 60; QString posString = QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0')); mPos->setText(posString); } void PlayerWidget::continuePlaying(QMediaPlayer::MediaStatus state){ if(state == QMediaPlayer::EndOfMedia){ next(); } } void PlayerWidget::expand(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); foreach(QModelIndex i, sel){ mView->expand(i); expandRecursive(i); } } void PlayerWidget::expandRecursive(const QModelIndex &idx){ const QStandardItemModel *model = static_cast(idx.model()); QStandardItem *item = model->itemFromIndex(idx); for(int i = 0; i < item->rowCount(); ++i){ QModelIndex cur = model->indexFromItem(item->child(i, 0)); if(cur.isValid()){ mView->expand(cur); } } } void PlayerWidget::readSettings(){ mStarting = true; QSettings s; int vol = s.value("volume").toInt(); mVolumeSlider->setValue(vol); QString dir = s.value("folderdir", QDir::homePath()).toString(); mCurDir = dir; mDir->setText(mCurDir); mStarting = false; } void PlayerWidget::writeSettings(){ QSettings s; s.setValue("volume", mVolumeSlider->value()); s.setValue("folderdir", mCurDir); } void PlayerWidget::aboutDlg(){ QString title = QString(tr("About %1")).arg(qApp->applicationName()); QString msg; msg.append(tr("

A basic music player without all the fancy stuff implemented in eg. Amarok

")); msg.append(tr("

Depends on PostgreSQL, Qt and TagLib")); QMessageBox::about(this, title, msg); }