331 lines
11 KiB
C++
331 lines
11 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
** Contact: http://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the examples of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:BSD$
|
|
** You may use this file under the terms of the BSD license as follows:
|
|
**
|
|
** "Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions are
|
|
** met:
|
|
** * Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** * Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in
|
|
** the documentation and/or other materials provided with the
|
|
** distribution.
|
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
|
** contributors may be used to endorse or promote products derived
|
|
** from this software without specific prior written permission.
|
|
**
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "musicplayer.h"
|
|
#include "volumebutton.h"
|
|
#include <QDebug>
|
|
#include <QtWidgets>
|
|
#include <QApplication>
|
|
#include <QSlider>
|
|
#include <QPainter>
|
|
|
|
|
|
|
|
MusicPlayer::MusicPlayer(QWidget *parent) : QWidget(parent)
|
|
{
|
|
setWindowState(Qt::WindowMaximized);
|
|
setWindowFlags(Qt::FramelessWindowHint);
|
|
|
|
createWidgets();
|
|
createShortcuts();
|
|
|
|
connect(&mediaPlayer, &QMediaPlayer::positionChanged, this, &MusicPlayer::updatePosition);
|
|
connect(&mediaPlayer, &QMediaPlayer::durationChanged, this, &MusicPlayer::updateDuration);
|
|
connect(&mediaPlayer, &QMediaObject::metaDataAvailableChanged, this, &MusicPlayer::updateInfo);
|
|
|
|
typedef void(QMediaPlayer::*ErrorSignal)(QMediaPlayer::Error);
|
|
connect(&mediaPlayer, static_cast<ErrorSignal>(&QMediaPlayer::error),
|
|
this, &MusicPlayer::handleError);
|
|
connect(&mediaPlayer, &QMediaPlayer::stateChanged,
|
|
this, &MusicPlayer::updateState);
|
|
|
|
setAcceptDrops(true);
|
|
}
|
|
|
|
QStringList MusicPlayer::supportedMimeTypes()
|
|
{
|
|
QStringList result = QMediaPlayer::supportedMimeTypes();
|
|
if (result.isEmpty())
|
|
result.append(QStringLiteral("audio/mpeg"));
|
|
return result;
|
|
}
|
|
|
|
void MusicPlayer::openFile()
|
|
{
|
|
QFileDialog fileDialog(this);
|
|
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
|
|
fileDialog.setWindowTitle(tr("Open File"));
|
|
fileDialog.setMimeTypeFilters(MusicPlayer::supportedMimeTypes());
|
|
fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MusicLocation).value(0, QDir::homePath()));
|
|
if (fileDialog.exec() == QDialog::Accepted)
|
|
{
|
|
playUrl(fileDialog.selectedUrls().constFirst());
|
|
}
|
|
|
|
}
|
|
|
|
void MusicPlayer::playUrl(const QUrl &url)
|
|
{
|
|
playButton->setEnabled(true);
|
|
if (url.isLocalFile()) {
|
|
const QString filePath = url.toLocalFile();
|
|
setWindowFilePath(filePath);
|
|
infoLabel->setText(QDir::toNativeSeparators(filePath));
|
|
fileName = QFileInfo(filePath).fileName();
|
|
} else {
|
|
setWindowFilePath(QString());
|
|
infoLabel->setText(url.toString());
|
|
fileName.clear();
|
|
}
|
|
mediaPlayer.setMedia(url);
|
|
mediaPlayer.play();
|
|
}
|
|
|
|
void MusicPlayer::togglePlayback()
|
|
{
|
|
if (mediaPlayer.mediaStatus() == QMediaPlayer::NoMedia)
|
|
openFile();
|
|
else if (mediaPlayer.state() == QMediaPlayer::PlayingState)
|
|
mediaPlayer.pause();
|
|
else
|
|
mediaPlayer.play();
|
|
}
|
|
|
|
void MusicPlayer::seekForward()
|
|
{
|
|
positionSlider->triggerAction(QSlider::SliderPageStepAdd);
|
|
}
|
|
|
|
void MusicPlayer::seekBackward()
|
|
{
|
|
positionSlider->triggerAction(QSlider::SliderPageStepSub);
|
|
}
|
|
|
|
//! [0]
|
|
bool MusicPlayer::event(QEvent *event)
|
|
{
|
|
return QWidget::event(event);
|
|
}
|
|
//! [0]
|
|
|
|
static bool canHandleDrop(const QDropEvent *event)
|
|
{
|
|
const QList<QUrl> urls = event->mimeData()->urls();
|
|
if (urls.size() != 1)
|
|
return false;
|
|
QMimeDatabase mimeDatabase;
|
|
return MusicPlayer::supportedMimeTypes().
|
|
contains(mimeDatabase.mimeTypeForUrl(urls.constFirst()).name());
|
|
}
|
|
|
|
void MusicPlayer::dragEnterEvent(QDragEnterEvent *event)
|
|
{
|
|
event->setAccepted(canHandleDrop(event));
|
|
}
|
|
|
|
void MusicPlayer::dropEvent(QDropEvent *event)
|
|
{
|
|
event->accept();
|
|
// playUrl(event->mimeData()->urls().constFirst());
|
|
}
|
|
|
|
void MusicPlayer::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
offset = event->globalPos() - pos();
|
|
event->accept();
|
|
}
|
|
|
|
void MusicPlayer::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
//move(event->globalPos() - offset);
|
|
event->accept();
|
|
}
|
|
|
|
void MusicPlayer::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
offset = QPoint();
|
|
event->accept();
|
|
}
|
|
|
|
void MusicPlayer::updateState(QMediaPlayer::State state)
|
|
{
|
|
if (state == QMediaPlayer::PlayingState) {
|
|
playButton->setToolTip(tr("Pause"));
|
|
playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
|
|
} else {
|
|
playButton->setToolTip(tr("Play"));
|
|
playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
|
|
}
|
|
}
|
|
|
|
static QString formatTime(qint64 timeMilliSeconds)
|
|
{
|
|
qint64 seconds = timeMilliSeconds / 1000;
|
|
const qint64 minutes = seconds / 60;
|
|
seconds -= minutes * 60;
|
|
return QStringLiteral("%1:%2")
|
|
.arg(minutes, 2, 10, QLatin1Char('0'))
|
|
.arg(seconds, 2, 10, QLatin1Char('0'));
|
|
}
|
|
|
|
void MusicPlayer::updatePosition(qint64 position)
|
|
|
|
{
|
|
positionSlider->setValue(position);
|
|
positionLabel->setText(formatTime(position));
|
|
}
|
|
|
|
void MusicPlayer::updateDuration(qint64 duration)
|
|
{
|
|
positionSlider->setRange(0, duration);
|
|
positionSlider->setEnabled(duration > 0);
|
|
positionSlider->setPageStep(duration / 10);
|
|
updateInfo();
|
|
}
|
|
|
|
|
|
void MusicPlayer::setPosition(int position)
|
|
{
|
|
// avoid seeking when the slider value change is triggered from updatePosition()
|
|
if (qAbs(mediaPlayer.position() - position) > 99)
|
|
{
|
|
mediaPlayer.setPosition(position);
|
|
}
|
|
}
|
|
|
|
void MusicPlayer::updateInfo()
|
|
{
|
|
QStringList info;
|
|
if (!fileName.isEmpty())
|
|
info.append(fileName);
|
|
if (mediaPlayer.isMetaDataAvailable()) {
|
|
QString author = mediaPlayer.metaData(QStringLiteral("Author")).toString();
|
|
if (!author.isEmpty())
|
|
info.append(author);
|
|
QString title = mediaPlayer.metaData(QStringLiteral("Title")).toString();
|
|
if (!title.isEmpty())
|
|
info.append(title);
|
|
}
|
|
info.append(formatTime(mediaPlayer.duration()));
|
|
infoLabel->setText(info.join(tr(" - ")));
|
|
}
|
|
|
|
void MusicPlayer::handleError()
|
|
{
|
|
playButton->setEnabled(false);
|
|
const QString errorString = mediaPlayer.errorString();
|
|
infoLabel->setText(errorString.isEmpty()
|
|
? tr("Unknown error #%1").arg(int(mediaPlayer.error()))
|
|
: tr("Error: %1").arg(errorString));
|
|
}
|
|
|
|
void MusicPlayer::createWidgets()
|
|
{
|
|
playButton = new QToolButton(this);
|
|
playButton->setEnabled(false);
|
|
playButton->setToolTip(tr("Play"));
|
|
playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
|
|
playButton->setFixedSize(55,35);
|
|
connect(playButton, &QAbstractButton::clicked, this, &MusicPlayer::togglePlayback);
|
|
|
|
QAbstractButton *openButton = new QToolButton(this);
|
|
openButton->setText(tr("..."));
|
|
openButton->setToolTip(tr("Open a file..."));
|
|
openButton->setFixedSize(55,35);
|
|
connect(openButton, &QAbstractButton::clicked, this, &MusicPlayer::openFile);
|
|
|
|
QPushButton *exitBtn = new QPushButton(this);
|
|
exitBtn->setText(tr("Exit"));
|
|
exitBtn->setFixedSize(55,35);
|
|
connect(exitBtn, &QPushButton::clicked, this, [=](){
|
|
close();
|
|
});
|
|
|
|
|
|
volumeButton = new VolumeButton(this);
|
|
volumeButton->setToolTip(tr("Adjust volume"));
|
|
volumeButton->setVolume(mediaPlayer.volume());
|
|
volumeButton->setFixedSize(55,35);
|
|
connect(volumeButton, &VolumeButton::volumeChanged, &mediaPlayer, &QMediaPlayer::setVolume);
|
|
|
|
positionSlider = new QSlider(Qt::Horizontal, this);
|
|
positionSlider->setEnabled(false);
|
|
positionSlider->setToolTip(tr("Seek"));
|
|
positionSlider->setMaximum(1000000);
|
|
positionSlider->setMinimum(0);
|
|
positionSlider->setValue(0);
|
|
positionSlider->setMinimumWidth(120);
|
|
connect(positionSlider, &QSlider::valueChanged, this, &MusicPlayer::setPosition);
|
|
|
|
infoLabel = new QLabel(this);
|
|
QFont font;
|
|
font.setPointSize(14);
|
|
infoLabel->setFont(font);
|
|
|
|
positionLabel = new QLabel(tr("00:00"), this);
|
|
positionLabel->setMinimumWidth(positionLabel->sizeHint().width());
|
|
|
|
QGridLayout *controlLayout = new QGridLayout(this);
|
|
controlLayout->addWidget(openButton,1,0);
|
|
controlLayout->addWidget(playButton,1,1);
|
|
controlLayout->addWidget(positionSlider,1,2);
|
|
controlLayout->addWidget(positionLabel,1,3);
|
|
controlLayout->addWidget(volumeButton,1,4);
|
|
controlLayout->addWidget(exitBtn, 1, 5);
|
|
controlLayout->addWidget(infoLabel,0,2);
|
|
controlLayout->setSpacing(25);
|
|
|
|
}
|
|
|
|
void MusicPlayer::createShortcuts()
|
|
{
|
|
QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this);
|
|
connect(quitShortcut, &QShortcut::activated, QCoreApplication::quit);
|
|
|
|
QShortcut *openShortcut = new QShortcut(QKeySequence::Open, this);
|
|
connect(openShortcut, &QShortcut::activated, this, &MusicPlayer::openFile);
|
|
|
|
QShortcut *toggleShortcut = new QShortcut(Qt::Key_Space, this);
|
|
connect(toggleShortcut, &QShortcut::activated, this, &MusicPlayer::togglePlayback);
|
|
|
|
QShortcut *forwardShortcut = new QShortcut(Qt::Key_Right, this);
|
|
connect(forwardShortcut, &QShortcut::activated, this, &MusicPlayer::seekForward);
|
|
|
|
QShortcut *backwardShortcut = new QShortcut(Qt::Key_Left, this);
|
|
connect(backwardShortcut, &QShortcut::activated, this, &MusicPlayer::seekBackward);
|
|
|
|
QShortcut *increaseShortcut = new QShortcut(Qt::Key_Up, this);
|
|
connect(increaseShortcut, &QShortcut::activated, volumeButton, &VolumeButton::increaseVolume);
|
|
|
|
QShortcut *decreaseShortcut = new QShortcut(Qt::Key_Down, this);
|
|
connect(decreaseShortcut, &QShortcut::activated, volumeButton, &VolumeButton::descreaseVolume);
|
|
}
|
|
|