Warning, file /multimedia/amarok/src/widgets/VolumeDial.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /**************************************************************************************** 0002 * Copyright (c) 2009 Thomas Luebking <thomas.luebking@web.de> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #include "VolumeDial.h" 0018 0019 #include "PaletteHandler.h" 0020 #include "SvgHandler.h" 0021 0022 #include <QConicalGradient> 0023 #include <QCoreApplication> 0024 #include <QMouseEvent> 0025 #include <QPainter> 0026 #include <QToolBar> 0027 #include <QToolTip> 0028 0029 #include <KColorUtils> 0030 #include <KLocalizedString> 0031 0032 #include <cmath> 0033 0034 VolumeDial::VolumeDial( QWidget *parent ) : QDial( parent ) 0035 , m_isClick( false ) 0036 , m_isDown( false ) 0037 , m_muted( false ) 0038 { 0039 m_anim.step = 0; 0040 m_anim.timer = 0; 0041 setMouseTracking( true ); 0042 0043 connect( this, &VolumeDial::valueChanged, this, &VolumeDial::valueChangedSlot ); 0044 connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &VolumeDial::paletteChanged ); 0045 } 0046 0047 void VolumeDial::addWheelProxies(const QList<QWidget *> &proxies ) 0048 { 0049 foreach ( QWidget *proxy, proxies ) 0050 { 0051 if ( !m_wheelProxies.contains( proxy ) ) 0052 { 0053 proxy->installEventFilter( this ); 0054 connect ( proxy, &QWidget::destroyed, this, &VolumeDial::removeWheelProxy ); 0055 m_wheelProxies << proxy; 0056 } 0057 } 0058 } 0059 0060 void VolumeDial::paletteChanged( const QPalette &palette ) 0061 { 0062 const QColor &fg = palette.color( foregroundRole() ); 0063 const QColor &hg = palette.color( QPalette::Highlight ); 0064 const qreal contrast = KColorUtils::contrastRatio( hg, palette.color( backgroundRole() ) ); 0065 m_highlightColor = KColorUtils::mix( hg, fg, 1.0 - contrast/3.0 ); 0066 renderIcons(); 0067 } 0068 0069 void VolumeDial::enterEvent( QEvent * ) 0070 { 0071 startFade(); 0072 } 0073 0074 // NOTICE: we intercept wheelEvents for ourself to prevent the tooltip hiding on them, 0075 // see ::wheelEvent() 0076 // this is _NOT_ redundant to the code in MainToolbar.cpp 0077 bool VolumeDial::eventFilter( QObject *o, QEvent *e ) 0078 { 0079 if ( e->type() == QEvent::Wheel && !static_cast<QWheelEvent*>(e)->modifiers() ) 0080 { 0081 if ( o == this || m_wheelProxies.contains( static_cast<QWidget*>( o ) ) ) 0082 { 0083 QWheelEvent *wev = static_cast<QWheelEvent*>(e); 0084 if ( o != this ) 0085 { 0086 QPoint pos( 0, 0 ); // the event needs to be on us or nothing will happen 0087 QWheelEvent nwev( pos, mapToGlobal( pos ), wev->pixelDelta(), wev->angleDelta(), wev->buttons(), wev->modifiers(), wev->phase(), wev->inverted(), wev->source() ); 0088 wheelEvent( &nwev ); 0089 } 0090 else 0091 wheelEvent( wev ); 0092 return true; 0093 } 0094 else // we're not needed globally anymore 0095 qApp->removeEventFilter( this ); 0096 } 0097 return false; 0098 } 0099 0100 void VolumeDial::leaveEvent( QEvent * ) 0101 { 0102 startFade(); 0103 } 0104 0105 static bool onRing( const QRect &r, const QPoint &p ) 0106 { 0107 const QPoint c = r.center(); 0108 const int dx = p.x() - c.x(); 0109 const int dy = p.y() - c.y(); 0110 return sqrt(dx*dx + dy*dy) > r.width()/4; 0111 } 0112 0113 void VolumeDial::mouseMoveEvent( QMouseEvent *me ) 0114 { 0115 if ( me->buttons() == Qt::NoButton ) 0116 setCursor( onRing( rect(), me->pos() ) ? Qt::PointingHandCursor : Qt::ArrowCursor ); 0117 else if ( m_isClick ) 0118 me->accept(); 0119 else 0120 QDial::mouseMoveEvent( me ); 0121 } 0122 0123 void VolumeDial::mousePressEvent( QMouseEvent *me ) 0124 { 0125 if ( me->button() != Qt::LeftButton ) 0126 { 0127 QDial::mousePressEvent( me ); 0128 return; 0129 } 0130 0131 m_isClick = !onRing( rect(), me->pos() ); 0132 0133 if ( m_isClick ) 0134 update(); // hide the ring 0135 else 0136 { 0137 setCursor( Qt::PointingHandCursor ); // hint dragging 0138 QDial::mousePressEvent( me ); // this will directly jump to the proper position 0139 } 0140 0141 // for value changes caused by mouseevent we'll only let our adjusted value changes be emitted 0142 // see ::sliderChange() 0143 m_formerValue = value(); 0144 blockSignals( true ); 0145 } 0146 0147 void VolumeDial::mouseReleaseEvent( QMouseEvent *me ) 0148 { 0149 if ( me->button() != Qt::LeftButton ) 0150 return; 0151 0152 blockSignals( false ); // free signals 0153 setCursor( Qt::ArrowCursor ); 0154 setSliderDown( false ); 0155 0156 if ( m_isClick ) 0157 { 0158 m_isClick = !onRing( rect(), me->pos() ); 0159 if ( m_isClick ) 0160 Q_EMIT muteToggled( !m_muted ); 0161 } 0162 0163 m_isClick = false; 0164 } 0165 0166 void VolumeDial::paintEvent( QPaintEvent * ) 0167 { 0168 QPainter p( this ); 0169 int icon = m_muted ? 0 : 3; 0170 if ( icon && value() < 66 ) 0171 icon = value() < 33 ? 1 : 2; 0172 p.setRenderHint( QPainter::SmoothPixmapTransform ); 0173 p.drawPixmap( 0,0, m_icon[ icon ].width()/2, m_icon[ icon ].height()/2, m_icon[ icon ] ); 0174 if ( !m_isClick ) 0175 { 0176 p.setPen( QPen( m_sliderGradient, 3, Qt::SolidLine, Qt::RoundCap ) ); 0177 p.setRenderHint( QPainter::Antialiasing ); 0178 p.drawArc( rect().adjusted(4,4,-4,-4), -110*16, - value()*320*16 / (maximum() - minimum()) ); 0179 } 0180 p.end(); 0181 } 0182 0183 void VolumeDial::removeWheelProxy( QObject *w ) 0184 { 0185 m_wheelProxies.removeOne( static_cast<QWidget*>(w) ); 0186 } 0187 0188 void VolumeDial::resizeEvent( QResizeEvent *re ) 0189 { 0190 if( width() != height() ) 0191 resize( height(), height() ); 0192 else 0193 QDial::resizeEvent( re ); 0194 0195 if( re->size() != re->oldSize() ) 0196 { 0197 renderIcons(); 0198 m_sliderGradient = QPixmap( size() ); 0199 updateSliderGradient(); 0200 update(); 0201 } 0202 } 0203 0204 void VolumeDial::renderIcons() 0205 { 0206 //double size svg render to have better looking high-dpi toolbar 0207 m_icon[0] = The::svgHandler()->renderSvg( "Muted", width()*2, height()*2, "Muted", true ); 0208 m_icon[1] = The::svgHandler()->renderSvg( "Volume_low", width()*2, height()*2, "Volume_low", true ); 0209 m_icon[2] = The::svgHandler()->renderSvg( "Volume_mid", width()*2, height()*2, "Volume_mid", true ); 0210 m_icon[3] = The::svgHandler()->renderSvg( "Volume", width()*2, height()*2, "Volume", true ); 0211 if( layoutDirection() == Qt::RightToLeft ) 0212 { 0213 for ( int i = 0; i < 4; ++i ) 0214 m_icon[i] = QPixmap::fromImage( m_icon[i].toImage().mirrored( true, false ) ); 0215 } 0216 } 0217 0218 void VolumeDial::startFade() 0219 { 0220 if ( m_anim.timer ) 0221 killTimer( m_anim.timer ); 0222 m_anim.timer = startTimer( 40 ); 0223 } 0224 0225 void VolumeDial::stopFade() 0226 { 0227 killTimer( m_anim.timer ); 0228 m_anim.timer = 0; 0229 if ( m_anim.step < 0 ) 0230 m_anim.step = 0; 0231 else if ( m_anim.step > 6 ) 0232 m_anim.step = 6; 0233 } 0234 0235 void VolumeDial::timerEvent( QTimerEvent *te ) 0236 { 0237 if ( te->timerId() != m_anim.timer ) 0238 return; 0239 if ( underMouse() ) // fade in 0240 { 0241 m_anim.step += 2; 0242 if ( m_anim.step > 5 ) 0243 stopFade(); 0244 } 0245 else // fade out 0246 { 0247 --m_anim.step; 0248 if ( m_anim.step < 1 ) 0249 stopFade(); 0250 } 0251 updateSliderGradient(); 0252 repaint(); 0253 } 0254 0255 void VolumeDial::updateSliderGradient() 0256 { 0257 m_sliderGradient.fill( Qt::transparent ); 0258 QColor c = m_highlightColor; 0259 if ( !m_anim.step ) 0260 { 0261 c.setAlpha( 99 ); 0262 m_sliderGradient.fill( c ); 0263 return; 0264 } 0265 0266 QConicalGradient cg( m_sliderGradient.rect().center(), -90 ); 0267 0268 c.setAlpha( 99 + m_anim.step*156/6 ); 0269 cg.setColorAt( 0, c ); 0270 c.setAlpha( 99 + m_anim.step*42/6 ); 0271 cg.setColorAt( 1, c ); 0272 0273 QPainter p( &m_sliderGradient ); 0274 p.fillRect( m_sliderGradient.rect(), cg ); 0275 p.end(); 0276 } 0277 0278 void VolumeDial::wheelEvent( QWheelEvent *wev ) 0279 { 0280 QDial::wheelEvent( wev ); 0281 wev->accept(); 0282 0283 const QPoint tooltipPosition = mapToGlobal( rect().translated( 7, -22 ).bottomLeft() ); 0284 QToolTip::showText( tooltipPosition, toolTip() ); 0285 0286 // NOTICE: this is a bit tricky. 0287 // the ToolTip "QTipLabel" just installed a global eventfilter that intercepts various 0288 // events and hides itself on them. Therefore every even wheelevent will close the tip 0289 // ("works - works not - works - works not - ...") 0290 // so we post-install our own global eventfilter to handle wheel events meant for us bypassing 0291 // the ToolTip eventfilter 0292 0293 // first remove to prevent multiple installations but ensure we're on top of the ToolTip filter 0294 qApp->removeEventFilter( this ); 0295 // it's ultimately removed in the timer triggered ::hideToolTip() slot 0296 qApp->installEventFilter( this ); 0297 } 0298 0299 void VolumeDial::setMuted( bool mute ) 0300 { 0301 m_muted = mute; 0302 0303 setToolTip( m_muted ? i18n( "Muted" ) : i18n( "Volume: %1%", value() ) ); 0304 update(); 0305 } 0306 0307 QSize VolumeDial::sizeHint() const 0308 { 0309 if ( QToolBar *toolBar = qobject_cast<QToolBar*>( parentWidget() ) ) 0310 return toolBar->iconSize(); 0311 0312 return QDial::sizeHint(); 0313 } 0314 0315 void VolumeDial::sliderChange( SliderChange change ) 0316 { 0317 if ( change == SliderValueChange && isSliderDown() && signalsBlocked() ) 0318 { 0319 int d = value() - m_formerValue; 0320 if ( d && d < 33 && d > -33 ) // don't allow real "jumps" > 1/3 0321 { 0322 if ( d > 5 ) // ease movement 0323 d = 5; 0324 else if ( d < -5 ) 0325 d = -5; 0326 m_formerValue += d; 0327 blockSignals( false ); 0328 Q_EMIT sliderMoved( m_formerValue ); 0329 Q_EMIT valueChanged( m_formerValue ); 0330 blockSignals( true ); 0331 } 0332 if ( d ) 0333 setValue( m_formerValue ); 0334 } 0335 QDial::sliderChange(change); 0336 } 0337 0338 void VolumeDial::valueChangedSlot( int v ) 0339 { 0340 m_isClick = false; 0341 0342 setToolTip( m_muted ? i18n( "Muted" ) : i18n( "Volume: %1%", v ) ); 0343 update(); 0344 } 0345