Files
cantata/widgets/volumeslider.cpp

319 lines
8.9 KiB
C++

/*
* Cantata
*
* Copyright (c) 2011-2017 Craig Drummond <craig.p.drummond@gmail.com>
*
* ----
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "volumeslider.h"
#include "mpd-interface/mpdconnection.h"
#include "mpd-interface/mpdstatus.h"
#include "support/action.h"
#include "support/actioncollection.h"
#include "gui/stdactions.h"
#include "support/utils.h"
#include "gui/settings.h"
#include <QStyle>
#include <QPainter>
#include <QPainterPath>
#include <QProxyStyle>
#include <QApplication>
#include <QLabel>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QMenu>
class VolumeSliderProxyStyle : public QProxyStyle
{
public:
VolumeSliderProxyStyle()
: QProxyStyle()
{
setBaseStyle(qApp->style());
}
int styleHint(StyleHint stylehint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const
{
if (SH_Slider_AbsoluteSetButtons==stylehint) {
return Qt::LeftButton|QProxyStyle::styleHint(stylehint, opt, widget, returnData);
} else {
return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
}
}
};
static int widthStep=4;
static int constHeightStep=2;
QColor VolumeSlider::clampColor(const QColor &col)
{
static const int constMin=64;
static const int constMax=240;
if (col.value()<constMin) {
return QColor(constMin, constMin, constMin);
} else if (col.value()>constMax) {
return QColor(constMax, constMax, constMax);
}
return col;
}
VolumeSlider::VolumeSlider(QWidget *p)
: QSlider(p)
, lineWidth(0)
, down(false)
, fadingStop(false)
, muteAction(0)
, menu(0)
{
widthStep=4;
setRange(0, 100);
setPageStep(Settings::self()->volumeStep());
lineWidth=Utils::scaleForDpi(1);
int w=lineWidth*widthStep*19;
int h=lineWidth*constHeightStep*10;
setFixedHeight(h+1);
setFixedWidth(w);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setOrientation(Qt::Horizontal);
setFocusPolicy(Qt::NoFocus);
setStyle(new VolumeSliderProxyStyle());
setStyleSheet(QString("QSlider::groove:horizontal {border: 0px;} "
"QSlider::sub-page:horizontal {border: 0px;} "
"QSlider::handle:horizontal {width: 0px; height:0px; margin:0;}"));
textCol=clampColor(palette().color(QPalette::Active, QPalette::Text));
generatePixmaps();
}
void VolumeSlider::initActions()
{
if (muteAction) {
return;
}
muteAction = ActionCollection::get()->createAction("mute", tr("Mute"));
addAction(muteAction);
connect(muteAction, SIGNAL(triggered()), MPDConnection::self(), SLOT(toggleMute()));
connect(MPDStatus::self(), SIGNAL(updated()), this, SLOT(updateMpdStatus()));
connect(StdActions::self()->increaseVolumeAction, SIGNAL(triggered()), this, SLOT(increaseVolume()));
connect(StdActions::self()->decreaseVolumeAction, SIGNAL(triggered()), this, SLOT(decreaseVolume()));
connect(this, SIGNAL(valueChanged(int)), MPDConnection::self(), SLOT(setVolume(int)));
addAction(StdActions::self()->increaseVolumeAction);
addAction(StdActions::self()->decreaseVolumeAction);
}
void VolumeSlider::setColor(QColor col)
{
col=Utils::clampColor(col);
if (col!=textCol) {
textCol=col;
generatePixmaps();
}
}
void VolumeSlider::paintEvent(QPaintEvent *)
{
bool reverse=isRightToLeft();
QPainter p(this);
bool muted=MPDConnection::self()->isMuted();
if (muted || !isEnabled()) {
p.setOpacity(0.25);
}
p.drawPixmap(0, 0, pixmaps[0]);
#if 1
int steps=(value()/10.0)+0.5;
if (steps>0) {
if (steps<10) {
int wStep=widthStep*lineWidth;
p.setClipRect(reverse
? QRect(width()-((steps*wStep*2)-wStep), 0, width(), height())
: QRect(0, 0, (steps*wStep*2)-wStep, height()));
p.setClipping(true);
}
p.drawPixmap(0, 0, pixmaps[1]);
if (steps<10) {
p.setClipping(false);
}
}
#else // Partial filling of each block?
if (value()>0) {
if (value()<100) {
int fillWidth=(width()*(0.01*value()))+0.5;
p.setClipRect(reverse
? QRect(width()-fillWidth, 0, width(), height())
: QRect(0, 0, fillWidth, height()));
p.setClipping(true);
}
p.drawPixmap(0, 0, *(pixmaps[1]));
if (value()<100) {
p.setClipping(false);
}
}
#endif
if (!muted) {
p.setOpacity(p.opacity()*0.75);
p.setPen(textCol);
QFont f(font());
f.setPixelSize(qMax(height()/2.5, 8.0));
p.setFont(f);
QRect r=rect();
bool rtl=isRightToLeft();
if (rtl) {
r.setX(widthStep*lineWidth*12);
} else {
r.setWidth(widthStep*lineWidth*7);
}
p.drawText(r, Qt::AlignRight, QString("%1%").arg(value()));
}
}
void VolumeSlider::mousePressEvent(QMouseEvent *ev)
{
if (Qt::MiddleButton==ev->buttons()) {
down=true;
} else {
QSlider::mousePressEvent(ev);
}
}
void VolumeSlider::mouseReleaseEvent(QMouseEvent *ev)
{
if (down) {
down=false;
muteAction->trigger();
update();
} else {
QSlider::mouseReleaseEvent(ev);
}
}
void VolumeSlider::contextMenuEvent(QContextMenuEvent *ev)
{
static const char *constValProp="val";
if (!menu) {
menu=new QMenu(this);
muteMenuAction=menu->addAction(tr("Mute"));
muteMenuAction->setProperty(constValProp, -1);
for (int i=0; i<11; ++i) {
menu->addAction(QString("%1%").arg(i*10))->setProperty(constValProp, i*10);
}
}
muteMenuAction->setText(MPDConnection::self()->isMuted() ? tr("Unmute") : tr("Mute"));
QAction *ret = menu->exec(mapToGlobal(ev->pos()));
if (ret) {
int val=ret->property(constValProp).toInt();
if (-1==val) {
muteAction->trigger();
} else {
setValue(val);
}
}
}
void VolumeSlider::wheelEvent(QWheelEvent *ev)
{
int numDegrees = ev->delta() / 8;
int numSteps = numDegrees / 15;
if (numSteps > 0) {
for (int i = 0; i < numSteps; ++i) {
increaseVolume();
}
} else {
for (int i = 0; i > numSteps; --i) {
decreaseVolume();
}
}
}
void VolumeSlider::updateMpdStatus()
{
if (fadingStop) {
return;
}
int volume=MPDStatus::self()->volume();
blockSignals(true);
if (volume<0) {
setValue(0);
} else {
int unmuteVolume=-1;
if (0==volume) {
unmuteVolume=MPDConnection::self()->unmuteVolume();
if (unmuteVolume>0) {
volume=unmuteVolume;
}
}
setEnabled(true);
setToolTip(unmuteVolume>0 ? tr("Volume %1% (Muted)").arg(volume) : tr("Volume %1%").arg(volume));
setValue(volume);
}
setEnabled(volume>=0);
setVisible(volume>=0);
update();
muteAction->setEnabled(isEnabled());
StdActions::self()->increaseVolumeAction->setEnabled(isEnabled());
StdActions::self()->decreaseVolumeAction->setEnabled(isEnabled());
blockSignals(false);
}
void VolumeSlider::increaseVolume()
{
triggerAction(QAbstractSlider::SliderPageStepAdd);
}
void VolumeSlider::decreaseVolume()
{
triggerAction(QAbstractSlider::SliderPageStepSub);
}
void VolumeSlider::generatePixmaps()
{
pixmaps[0]=generatePixmap(false);
pixmaps[1]=generatePixmap(true);
}
QPixmap VolumeSlider::generatePixmap(bool filled)
{
bool reverse=isRightToLeft();
QPixmap pix(size());
pix.fill(Qt::transparent);
QPainter p(&pix);
p.setPen(textCol);
for (int i=0; i<10; ++i) {
int barHeight=(lineWidth*constHeightStep)*(i+1);
QRect r(reverse ? pix.width()-(widthStep+(i*lineWidth*widthStep*2))
: i*lineWidth*widthStep*2,
pix.height()-(barHeight+1), (lineWidth*widthStep)-1, barHeight);
if (filled) {
p.fillRect(r.adjusted(1, 1, 0, 0), textCol);
} else if (lineWidth>1) {
p.drawRect(r);
p.drawRect(r.adjusted(1, 1, -1, -1));
} else {
p.drawRect(r);
}
}
return pix;
}