File indexing completed on 2024-05-12 17:12:39

0001 /***************************************************************************
0002  *  Copyright (C) 2009 by Renaud Guezennec                                 *
0003  *   https://rolisteam.org/contact                   *
0004  *                                                                         *
0005  *   This software is free software; you can redistribute it and/or modify *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 #include <QFileDialog>
0021 #include <QInputDialog>
0022 #include <QMessageBox>
0023 #include <QMimeData>
0024 
0025 #include "playerwidget.h"
0026 
0027 #include "controller/audioplayercontroller.h"
0028 #include "ui_playerwidget.h"
0029 #define FACTOR_WAIT 4
0030 
0031 QString getExistingFile(const QString& rootDir, const QString& pathOnGM)
0032 {
0033     QFileInfo info(pathOnGM);
0034 
0035     auto list= pathOnGM.split("/");
0036     QStringList result;
0037     for(auto& item : list)
0038     {
0039         result.prepend(item);
0040     }
0041     QString consumedPath= "";
0042 
0043     for(auto& item : result)
0044     {
0045         if(consumedPath.isEmpty())
0046             consumedPath= item;
0047         else
0048             consumedPath= item + "/" + consumedPath;
0049         auto temp= QStringLiteral("%1/%2").arg(rootDir, consumedPath);
0050         if(QFile::exists(temp))
0051         {
0052             return temp;
0053         }
0054     }
0055     return rootDir + "/" + info.fileName(); // error message
0056 }
0057 
0058 PlayerWidget::PlayerWidget(AudioPlayerController* ctrl, QWidget* parent)
0059     : QWidget(parent), m_ctrl(ctrl), m_ui(new Ui::PlayerWidgetUI)
0060 {
0061     setAcceptDrops(true);
0062 
0063     m_ui->setupUi(this);
0064 
0065     if(!m_ctrl)
0066         return;
0067 
0068     setupUi();
0069 
0070     m_ui->m_songList->setModel(m_ctrl->model());
0071 
0072     connect(m_ctrl, &AudioPlayerController::localIsGmChanged, this, &PlayerWidget::updateUi);
0073     updateUi();
0074 }
0075 
0076 void PlayerWidget::setupUi()
0077 {
0078 
0079     m_ui->m_timeSlider->setMinimum(0);
0080     m_ui->m_volumeSlider->setRange(0, 100);
0081     // **************** CREATE ACTIONS ********************************
0082     m_playAct= new QAction(style()->standardIcon(QStyle::SP_MediaPlay), tr("Play"), this);
0083     m_pauseAct= new QAction(style()->standardIcon(QStyle::SP_MediaPause), tr("Pause"), this);
0084     m_stopAct= new QAction(style()->standardIcon(QStyle::SP_MediaStop), tr("Stop"), this);
0085 
0086     m_uniqueAct= new QAction(QIcon::fromTheme("playunique"), tr("Stop at the end"), this);
0087     m_uniqueAct->setShortcut(QKeySequence("Ctrl+U"));
0088     m_uniqueAct->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0089     m_uniqueAct->setCheckable(true);
0090 
0091     m_repeatAct= new QAction(QIcon::fromTheme("playloop"), tr("Play in loop"), this);
0092     m_repeatAct->setCheckable(true);
0093     m_repeatAct->setShortcut(QKeySequence("Ctrl+R"));
0094     m_repeatAct->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0095 
0096     m_shuffleAct= new QAction(QIcon::fromTheme("shuffle_mode"), tr("Shuffle Mode"), this);
0097     m_shuffleAct->setCheckable(true);
0098     m_shuffleAct->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0099 
0100     m_changeDirectoryAct= new QAction(style()->standardIcon(QStyle::SP_DirIcon), tr("Open Directory"), this);
0101     connect(m_changeDirectoryAct, &QAction::triggered, this, &PlayerWidget::changePlayerDirectory);
0102 
0103     m_volumeMutedAct= new QAction(tr("Mute Volume"), this);
0104     m_volumeMutedAct->setCheckable(true);
0105     m_volumeMutedAct->setShortcut(QKeySequence("Ctrl+M"));
0106     m_volumeMutedAct->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0107     m_loadTableTopAudioPlayListAct= new QAction(tr("load TableTopAudio.com playlist"), this);
0108 
0109     m_openPlayList= new QAction(style()->standardIcon(QStyle::SP_DialogOpenButton), tr("Open Playlist"), this);
0110     m_openPlayList->setShortcut(QKeySequence("Ctrl+J"));
0111     m_openPlayList->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0112     m_savePlayList= new QAction(style()->standardIcon(QStyle::SP_DialogSaveButton), tr("Save Playlist"), this);
0113     m_savePlayList->setShortcut(QKeySequence("Ctrl+E"));
0114     m_savePlayList->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0115     m_clearList= new QAction(style()->standardIcon(QStyle::SP_DialogResetButton), tr("Clear"), this);
0116     m_clearList->setShortcut(QKeySequence("Ctrl+Del"));
0117     m_clearList->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0118 
0119     m_addAction= new QAction(QIcon::fromTheme("add"), tr("Add Songs"), this);
0120     m_addAction->setShortcut(QKeySequence("Ctrl+A"));
0121     m_addAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0122     m_addStreamAction= new QAction(tr("Open Stream"), this);
0123 
0124     m_deleteAction= new QAction(QIcon::fromTheme("remove"), tr("Remove Song"), this);
0125     m_deleteAction->setShortcut(QKeySequence("Del"));
0126     m_deleteAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0127 
0128     if(m_ctrl)
0129         m_ui->m_volumeSlider->setValue(m_ctrl->volume());
0130     // m_audioFileFilter= m_preferences->value("AudioFileFilter", "*.wav *.mp2 *.mp3 *.ogg *.flac").toString();
0131 
0132     // **************** Add ACTIONS ********************************
0133 
0134     addAction(m_uniqueAct);
0135     addAction(m_repeatAct);
0136     addAction(m_shuffleAct);
0137     addAction(m_volumeMutedAct);
0138     addAction(m_openPlayList);
0139     addAction(m_savePlayList);
0140     addAction(m_clearList);
0141     addAction(m_deleteAction);
0142     addAction(m_addAction);
0143     addAction(m_addStreamAction);
0144 
0145     // **************** TOOLTIP ACTIONS ********************************
0146     m_addAction->setToolTip(tr("Add song to the list"));
0147     m_deleteAction->setToolTip(tr("Remove selected file"));
0148 
0149     // **************** Add Action In Buttons ********************************
0150     m_ui->m_deleteButton->setDefaultAction(m_deleteAction);
0151     m_ui->m_deleteButton->addAction(m_clearList);
0152     m_ui->m_savePlaylist->setDefaultAction(m_savePlayList);
0153     m_ui->m_addButton->setDefaultAction(m_addAction);
0154     m_ui->m_addButton->addAction(m_openPlayList);
0155     m_ui->m_addButton->addAction(m_loadTableTopAudioPlayListAct);
0156     m_ui->m_addButton->addAction(m_addStreamAction);
0157 
0158     m_ui->m_volumeMutedButton->setDefaultAction(m_volumeMutedAct);
0159     m_ui->m_playButton->setDefaultAction(m_playAct);
0160     m_ui->m_pauseButton->setDefaultAction(m_pauseAct);
0161     m_ui->m_stopButton->setDefaultAction(m_stopAct);
0162     m_ui->m_uniqueMode->setDefaultAction(m_uniqueAct);
0163     m_ui->m_shuffleBtn->setDefaultAction(m_shuffleAct);
0164     m_ui->m_repeatMode->setDefaultAction(m_repeatAct);
0165     m_ui->m_changeDirectory->setDefaultAction(m_changeDirectoryAct);
0166 
0167     // **************** CONNECT ********************************
0168     connect(m_clearList, &QAction::triggered, m_ctrl, &AudioPlayerController::clear);
0169     connect(m_addAction, &QAction::triggered, this, &PlayerWidget::addFiles);
0170     connect(m_openPlayList, &QAction::triggered, this, &PlayerWidget::openPlayList);
0171     connect(m_addStreamAction, &QAction::triggered, this, &PlayerWidget::openStream);
0172     connect(m_deleteAction, &QAction::triggered, this,
0173             [this]() { m_ctrl->removeSong(m_ui->m_songList->selectionModel()->selectedIndexes()); });
0174     connect(m_ui->m_songList, &QListView::doubleClicked, m_ctrl, [this](const QModelIndex& index) {
0175         m_ctrl->setMedia(index);
0176         m_ctrl->play();
0177     });
0178 
0179     updateIcon();
0180     // connect(&m_player, SIGNAL(durationChanged(qint64)), this, SLOT(setDuration(qint64)));
0181     connect(m_playAct, &QAction::triggered, this, [this]() {
0182         if(m_ctrl->state() == AudioPlayerController::PausedState)
0183             m_ctrl->play();
0184         else
0185         {
0186             auto index= m_ui->m_songList->currentIndex();
0187             if(!index.isValid())
0188                 return;
0189             m_ctrl->setMedia(index);
0190             m_ctrl->play();
0191         }
0192     }); //&AudioPlayerController::play
0193     connect(m_stopAct, &QAction::triggered, m_ctrl, &AudioPlayerController::stop);
0194     connect(m_pauseAct, &QAction::triggered, m_ctrl, &AudioPlayerController::pause);
0195 
0196     // time
0197     connect(m_ctrl, &AudioPlayerController::timeChanged, m_ui->m_timeSlider,
0198             [this]() { m_ui->m_timeSlider->setValue(m_ctrl->time()); });
0199     connect(m_ui->m_timeSlider, &QSlider::sliderMoved, m_ctrl, [this](qint64 time){
0200 
0201         if(!m_ui->m_timeSlider || !m_ui->m_timeSlider->isVisible())
0202             return;
0203 
0204         m_ctrl->setTime(time);
0205     });
0206 
0207     // Volume
0208     /*connect(m_ctrl, &AudioPlayerController::volumeChanged, m_ui->m_volumeSlider,
0209             [this]() { m_ui->m_volumeSlider->setValue(m_ctrl->volume()); });*/
0210     connect(m_ctrl, &AudioPlayerController::volumeChanged, m_ui->m_volumeSlider, &QSlider::setValue);
0211     connect(m_ui->m_volumeSlider, &QSlider::sliderMoved, m_ctrl, &AudioPlayerController::setVolume);
0212     // Mute
0213     connect(m_ctrl, &AudioPlayerController::mutedChanged, this, &PlayerWidget::updateIcon);
0214     connect(m_volumeMutedAct, &QAction::triggered, m_ctrl, &AudioPlayerController::mute);
0215 
0216     // duration
0217     connect(m_ctrl, &AudioPlayerController::durationChanged, m_ui->m_timeSlider, &QSlider::setMaximum);
0218     connect(m_ctrl, &AudioPlayerController::timeChanged, m_ui->m_timerDisplay, [this](qint64 position) {
0219         auto posInSecond= position / 1000;
0220         auto min= posInSecond / 60;
0221         auto second= posInSecond - (min * 60);
0222         if(min > 0)
0223             m_ui->m_timerDisplay->setText(
0224                 QString("%1min%2s").arg(min).arg(second, 2, 10, QLatin1Char('0'))); //, 3, 10, QLatin1Char('0')
0225         else
0226             m_ui->m_timerDisplay->setText(QString("%1s").arg(second, 2, 10, QLatin1Char('0')));
0227     });
0228     // QOverload<int>::of(&QLCDNumber::display));
0229     connect(m_ctrl, &AudioPlayerController::visibleChanged, this, &PlayerWidget::setVisible);
0230     setVisible(m_ctrl->visible());
0231 
0232     connect(m_ctrl, &AudioPlayerController::errorChanged, this, [this]() {
0233         m_ui->m_label->setStyleSheet("color: red");
0234         m_ui->m_label->setEchoMode(QLineEdit::Normal);
0235         m_ui->m_label->setText(m_ctrl->error());
0236     });
0237     connect(m_ctrl, &AudioPlayerController::textChanged, this, [this]() {
0238         m_ui->m_label->setStyleSheet("color: black");
0239         if(!m_ctrl->localIsGm())
0240             m_ui->m_label->setEchoMode(QLineEdit::Password);
0241         m_ui->m_label->setText(m_ctrl->text());
0242     });
0243 
0244     connect(m_repeatAct, &QAction::triggered, this,
0245             [this](bool b) { m_ctrl->setPlayingMode(b ? AudioPlayerController::LOOP : AudioPlayerController::NEXT); });
0246     connect(m_uniqueAct, &QAction::triggered, this, [this](bool b) {
0247         m_ctrl->setPlayingMode(b ? AudioPlayerController::UNIQUE : AudioPlayerController::NEXT);
0248     });
0249     connect(m_shuffleAct, &QAction::triggered, this, [this](bool b) {
0250         m_ctrl->setPlayingMode(b ? AudioPlayerController::SHUFFLE : AudioPlayerController::NEXT);
0251     });
0252 
0253     connect(m_loadTableTopAudioPlayListAct, &QAction::triggered, this, &PlayerWidget::openPlayList);
0254     connect(m_savePlayList, &QAction::triggered, this, &PlayerWidget::savePlaylist);
0255 
0256     connect(m_ctrl, &AudioPlayerController::stateChanged, this, [this]() {
0257         auto state= m_ctrl->state();
0258         m_playAct->setEnabled(state == AudioPlayerController::PausedState
0259                               || state == AudioPlayerController::StoppedState);
0260 
0261         m_pauseAct->setEnabled(state == AudioPlayerController::PlayingState
0262                                || state == AudioPlayerController::StoppedState);
0263         m_stopAct->setEnabled(state == AudioPlayerController::PlayingState);
0264     });
0265 
0266     auto updateMode= [this]() {
0267         auto m= m_ctrl->mode();
0268         m_uniqueAct->setChecked(m == AudioPlayerController::UNIQUE);
0269         m_shuffleAct->setChecked(m == AudioPlayerController::SHUFFLE);
0270         m_repeatAct->setChecked(m == AudioPlayerController::LOOP);
0271     };
0272     connect(m_ctrl, &AudioPlayerController::modeChanged, this, updateMode);
0273 
0274     updateMode();
0275 }
0276 
0277 void PlayerWidget::addFiles()
0278 {
0279     auto fileList
0280         = QFileDialog::getOpenFileUrls(this, tr("Add song"), QUrl(), tr("Audio files (%1)").arg("*.mp3 *.flac *.webm"));
0281     if(fileList.isEmpty())
0282         return;
0283     m_ctrl->addSong(fileList);
0284 }
0285 bool PlayerWidget::askToDeleteAll()
0286 {
0287     if(m_ctrl->model()->rowCount() == 0)
0288         return true;
0289 
0290     return (
0291         QMessageBox::Ok
0292         == QMessageBox::warning(this, tr("Attention!"),
0293                                 tr("You are about to load an new playlist. All previously load file will be dropped."),
0294                                 QMessageBox::Ok, QMessageBox::Cancel));
0295 }
0296 void PlayerWidget::openPlayList()
0297 {
0298     if(!askToDeleteAll())
0299     {
0300         return;
0301     }
0302     QString filename= QFileDialog::getOpenFileName(this, tr("Open Play List"), "", tr("PlayList (*.m3u)"));
0303     if(filename.isEmpty())
0304         return;
0305 
0306     m_ctrl->loadPlayList(filename);
0307 }
0308 void PlayerWidget::openStream()
0309 {
0310     bool a;
0311     QString value= QInputDialog::getText(this, tr("Open audio Stream"), tr("URL"), QLineEdit::Normal, QString(), &a);
0312     if(!a)
0313         return;
0314 
0315     QUrl url(value);
0316     if(!url.isValid())
0317         return;
0318 
0319     m_ctrl->addSong({url});
0320 }
0321 
0322 void PlayerWidget::contextMenuEvent(QContextMenuEvent* ev)
0323 {
0324     ev->ignore();
0325 }
0326 void PlayerWidget::addActionsIntoMenu(QMenu* menu)
0327 {
0328     menu->addAction(m_playAct);
0329     menu->addAction(m_pauseAct);
0330     menu->addAction(m_stopAct);
0331     menu->addSeparator();
0332     menu->addAction(m_addAction);
0333     menu->addAction(m_addStreamAction);
0334     menu->addAction(m_openPlayList);
0335     menu->addAction(m_loadTableTopAudioPlayListAct);
0336     menu->addAction(m_savePlayList);
0337     menu->addAction(m_clearList);
0338     menu->addAction(m_deleteAction);
0339     menu->addSeparator();
0340 }
0341 
0342 void PlayerWidget::updateUi()
0343 {
0344     auto isGM= m_ctrl->localIsGm();
0345     m_ui->m_playButton->setVisible(isGM);
0346     m_ui->m_stopButton->setVisible(isGM);
0347     m_ui->m_pauseButton->setVisible(isGM);
0348     m_ui->m_uniqueMode->setVisible(isGM);
0349     m_ui->m_repeatMode->setVisible(isGM);
0350     m_ui->m_shuffleBtn->setVisible(isGM);
0351     m_ui->m_timeSlider->setVisible(isGM);
0352     m_ui->m_addButton->setVisible(isGM);
0353     m_ui->m_deleteButton->setVisible(isGM);
0354     m_ui->m_songList->setVisible(isGM);
0355     m_ui->m_savePlaylist->setVisible(isGM);
0356     m_ui->m_changeDirectory->setVisible(!isGM);
0357     m_ui->m_timerDisplay->setVisible(isGM);
0358     m_ui->m_volumeSlider->setValue(m_ctrl->volume());
0359 }
0360 
0361 void PlayerWidget::updateIcon()
0362 {
0363     m_volumeMutedAct->setIcon(
0364         style()->standardIcon(m_ctrl->muted() ? QStyle::SP_MediaVolumeMuted : QStyle::SP_MediaVolume));
0365     m_volumeMutedAct->setChecked(m_ctrl->muted());
0366 }
0367 
0368 void PlayerWidget::savePlaylist()
0369 {
0370     QString filename= QFileDialog::getSaveFileName(this, tr("Save Play List"), "", tr("PlayList (*.m3u)"));
0371     if(filename.isEmpty())
0372         return;
0373 
0374     m_ctrl->loadPlayList(filename);
0375 }
0376 
0377 void PlayerWidget::dropEvent(QDropEvent* event)
0378 {
0379     const QMimeData* mimeData= event->mimeData();
0380     if(mimeData->hasUrls())
0381     {
0382         QList<QUrl> list= mimeData->urls();
0383         for(auto& url : list)
0384         {
0385             if(url.toLocalFile().endsWith(".m3u")) // play list
0386             {
0387                 m_ctrl->loadPlayList(url.toLocalFile());
0388             }
0389             else
0390             {
0391                 m_ctrl->addSong({url.toLocalFile()});
0392             }
0393         }
0394         event->acceptProposedAction();
0395     }
0396 }