Warning, file /multimedia/amarok/src/widgets/SliderWidget.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) 2003-2009 Mark Kretschmann <kretschmann@kde.org> * 0003 * Copyright (c) 2005 Gabor Lehel <illissius@gmail.com> * 0004 * Copyright (c) 2008 Dan Meltzer <parallelgrapefruit@gmail.com> * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify it under * 0007 * the terms of the GNU General Public License as published by the Free Software * 0008 * Foundation; either version 2 of the License, or (at your option) any later * 0009 * version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0013 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License along with * 0016 * this program. If not, see <http://www.gnu.org/licenses/>. * 0017 ****************************************************************************************/ 0018 0019 #include "SliderWidget.h" 0020 0021 #include <config.h> 0022 0023 #include "core/support/Amarok.h" 0024 #include "amarokurls/AmarokUrlHandler.h" 0025 #include "amarokconfig.h" 0026 #include "App.h" 0027 #include "BookmarkTriangle.h" 0028 #include "core/support/Debug.h" 0029 #include "EngineController.h" 0030 #include "core/meta/support/MetaUtility.h" 0031 #include "SvgHandler.h" 0032 #include "ProgressWidget.h" 0033 0034 #include <QIcon> 0035 #include <KLocalizedString> 0036 #include <QStandardPaths> 0037 0038 #include <QAction> 0039 #include <QContextMenuEvent> 0040 #include <QFontMetrics> 0041 #include <QMenu> 0042 #include <QStyle> 0043 #include <QStyleOption> 0044 #include <QPainter> 0045 0046 Amarok::Slider::Slider( Qt::Orientation orientation, uint max, QWidget *parent ) 0047 : QSlider( orientation, parent ) 0048 , m_sliding( false ) 0049 , m_outside( false ) 0050 , m_prevValue( 0 ) 0051 , m_needsResize( true ) 0052 { 0053 setMouseTracking( true ); 0054 setRange( 0, max ); 0055 setAttribute( Qt::WA_NoMousePropagation, true ); 0056 setAttribute( Qt::WA_Hover, true ); 0057 if ( orientation == Qt::Vertical ) 0058 { 0059 setInvertedAppearance( true ); 0060 setInvertedControls( true ); 0061 } 0062 } 0063 0064 QRect 0065 Amarok::Slider::sliderHandleRect( const QRect &slider, qreal percent ) const 0066 { 0067 QRect rect; 0068 const bool inverse = ( orientation() == Qt::Horizontal ) ? 0069 ( invertedAppearance() != (layoutDirection() == Qt::RightToLeft) ) : 0070 ( !invertedAppearance() ); 0071 0072 if( m_usingCustomStyle) 0073 rect = The::svgHandler()->sliderKnobRect( slider, percent, inverse ); 0074 else 0075 { 0076 if ( inverse ) 0077 percent = 1.0 - percent; 0078 const int handleSize = style()->pixelMetric( QStyle::PM_SliderControlThickness ); 0079 rect = QRect( 0, 0, handleSize, handleSize ); 0080 rect.moveTo( slider.x() + qRound( ( slider.width() - handleSize ) * percent ), slider.y() + 1 ); 0081 } 0082 0083 return rect; 0084 } 0085 0086 void 0087 Amarok::Slider::wheelEvent( QWheelEvent *e ) 0088 { 0089 DEBUG_BLOCK 0090 0091 if( orientation() == Qt::Vertical ) 0092 { 0093 // Will be handled by the parent widget 0094 e->ignore(); 0095 return; 0096 } 0097 0098 // Position Slider (horizontal) 0099 // only used for progress slider now! 0100 int step = e->angleDelta().y() * 24; //FIXME: check if .x() must be used 0101 int nval = value() + step; 0102 nval = qMax(nval, minimum()); 0103 nval = qMin(nval, maximum()); 0104 0105 QSlider::setValue( nval ); 0106 0107 Q_EMIT sliderReleased( value() ); 0108 } 0109 0110 void 0111 Amarok::Slider::mouseMoveEvent( QMouseEvent *e ) 0112 { 0113 if ( m_sliding ) 0114 { 0115 //feels better, but using set value of 20 is bad of course 0116 QRect rect( -20, -20, width()+40, height()+40 ); 0117 0118 if ( orientation() == Qt::Horizontal && !rect.contains( e->pos() ) ) 0119 { 0120 if ( !m_outside ) 0121 { 0122 QSlider::setValue( m_prevValue ); 0123 //if mouse released outside of slider, Q_EMIT sliderMoved to previous value 0124 Q_EMIT sliderMoved( m_prevValue ); 0125 } 0126 m_outside = true; 0127 } 0128 else 0129 { 0130 m_outside = false; 0131 slideEvent( e ); 0132 Q_EMIT sliderMoved( value() ); 0133 } 0134 } 0135 else 0136 QSlider::mouseMoveEvent( e ); 0137 } 0138 0139 void 0140 Amarok::Slider::slideEvent( QMouseEvent *e ) 0141 { 0142 QRect knob; 0143 if ( maximum() > minimum() ) 0144 knob = sliderHandleRect( rect(), ((qreal)value()) / ( maximum() - minimum() ) ); 0145 0146 int position; 0147 int span; 0148 0149 if( orientation() == Qt::Horizontal ) 0150 { 0151 position = e->pos().x() - knob.width() / 2; 0152 span = width() - knob.width(); 0153 } 0154 else 0155 { 0156 position = e->pos().y() - knob.height() / 2; 0157 span = height() - knob.height(); 0158 } 0159 0160 const bool inverse = ( orientation() == Qt::Horizontal ) ? 0161 ( invertedAppearance() != (layoutDirection() == Qt::RightToLeft) ) : 0162 ( !invertedAppearance() ); 0163 const int val = QStyle::sliderValueFromPosition( minimum(), maximum(), position, span, inverse ); 0164 QSlider::setValue( val ); 0165 } 0166 0167 void 0168 Amarok::Slider::mousePressEvent( QMouseEvent *e ) 0169 { 0170 m_sliding = true; 0171 m_prevValue = value(); 0172 0173 QRect knob; 0174 if ( maximum() > minimum() ) 0175 knob = sliderHandleRect( rect(), ((qreal)value()) / ( maximum() - minimum() ) ); 0176 if ( !knob.contains( e->pos() ) ) 0177 mouseMoveEvent( e ); 0178 } 0179 0180 void 0181 Amarok::Slider::mouseReleaseEvent( QMouseEvent* ) 0182 { 0183 if( !m_outside && value() != m_prevValue ) 0184 Q_EMIT sliderReleased( value() ); 0185 0186 m_sliding = false; 0187 m_outside = false; 0188 } 0189 0190 void 0191 Amarok::Slider::setValue( int newValue ) 0192 { 0193 //don't adjust the slider while the user is dragging it! 0194 if ( !m_sliding || m_outside ) 0195 QSlider::setValue( newValue ); 0196 else 0197 m_prevValue = newValue; 0198 } 0199 0200 void Amarok::Slider::paintCustomSlider( QPainter *p, bool paintMoodbar ) 0201 { 0202 qreal percent = 0.0; 0203 if ( maximum() > minimum() ) 0204 percent = ((qreal)value()) / ( maximum() - minimum() ); 0205 QStyleOptionSlider opt; 0206 initStyleOption( &opt ); 0207 if ( m_sliding || 0208 ( underMouse() && sliderHandleRect( rect(), percent ).contains( mapFromGlobal(QCursor::pos()) ) ) ) 0209 { 0210 opt.activeSubControls |= QStyle::SC_SliderHandle; 0211 } 0212 The::svgHandler()->paintCustomSlider( p, &opt, percent, paintMoodbar ); 0213 } 0214 0215 0216 ////////////////////////////////////////////////////////////////////////////////////////// 0217 /// CLASS VolumeSlider 0218 ////////////////////////////////////////////////////////////////////////////////////////// 0219 0220 Amarok::VolumeSlider::VolumeSlider( uint max, QWidget *parent, bool customStyle ) 0221 : Amarok::Slider( customStyle ? Qt::Horizontal : Qt::Vertical, max, parent ) 0222 { 0223 m_usingCustomStyle = customStyle; 0224 setFocusPolicy( Qt::NoFocus ); 0225 setInvertedAppearance( false ); 0226 setInvertedControls( false ); 0227 } 0228 0229 void 0230 Amarok::VolumeSlider::mousePressEvent( QMouseEvent *e ) 0231 { 0232 if( e->button() != Qt::RightButton ) 0233 { 0234 Amarok::Slider::mousePressEvent( e ); 0235 slideEvent( e ); 0236 } 0237 } 0238 0239 void 0240 Amarok::VolumeSlider::contextMenuEvent( QContextMenuEvent *e ) 0241 { 0242 QMenu menu; 0243 menu.setTitle( i18n( "Volume" ) ); 0244 menu.addAction( i18n( "100%" ) )->setData( 100 ); 0245 menu.addAction( i18n( "80%" ) )->setData( 80 ); 0246 menu.addAction( i18n( "60%" ) )->setData( 60 ); 0247 menu.addAction( i18n( "40%" ) )->setData( 40 ); 0248 menu.addAction( i18n( "20%" ) )->setData( 20 ); 0249 menu.addAction( i18n( "0%" ) )->setData( 0 ); 0250 0251 /* 0252 // TODO: Phonon 0253 menu.addSeparator(); 0254 menu.addAction( QIcon::fromTheme( "view-media-equalizer-amarok" ), i18n( "&Equalizer" ), qApp, &QCoreApplication::slotConfigEqualizer()) )->setData( -1 ); 0255 */ 0256 0257 QAction* a = menu.exec( mapToGlobal( e->pos() ) ); 0258 if( a ) 0259 { 0260 const int n = a->data().toInt(); 0261 if( n >= 0 ) 0262 { 0263 QSlider::setValue( n ); 0264 Q_EMIT sliderReleased( n ); 0265 } 0266 } 0267 } 0268 0269 void 0270 Amarok::VolumeSlider::wheelEvent( QWheelEvent *e ) 0271 { 0272 const uint step = e->angleDelta().y() / Amarok::VOLUME_SENSITIVITY; //FIXME: check if .x() must be used 0273 QSlider::setValue( QSlider::value() + step ); 0274 0275 Q_EMIT sliderReleased( value() ); 0276 } 0277 0278 void 0279 Amarok::VolumeSlider::paintEvent( QPaintEvent *event ) 0280 { 0281 if( m_usingCustomStyle ) 0282 { 0283 QPainter p( this ); 0284 paintCustomSlider( &p ); 0285 p.end(); 0286 return; 0287 } 0288 0289 QSlider::paintEvent( event ); 0290 } 0291 0292 0293 ////////////////////////////////////////////////////////////////////////////////////////// 0294 ////////////////////////////////// TIMESLIDER //////////////////////////////////////////// 0295 ////////////////////////////////////////////////////////////////////////////////////////// 0296 0297 Amarok::TimeSlider::TimeSlider( QWidget *parent ) 0298 : Amarok::Slider( Qt::Horizontal, 0, parent ) 0299 , m_triangles() 0300 , m_knobX( 0.0 ) 0301 { 0302 m_usingCustomStyle = true; 0303 setFocusPolicy( Qt::NoFocus ); 0304 } 0305 0306 void 0307 Amarok::TimeSlider::setSliderValue( int value ) 0308 { 0309 Amarok::Slider::setValue( value ); 0310 } 0311 0312 void 0313 Amarok::TimeSlider::paintEvent( QPaintEvent *pe ) 0314 { 0315 QPainter p( this ); 0316 p.setClipRegion( pe->region() ); 0317 paintCustomSlider( &p, AmarokConfig::showMoodbarInSlider() ); 0318 p.end(); 0319 0320 } 0321 0322 void Amarok::TimeSlider::resizeEvent(QResizeEvent * event) 0323 { 0324 Amarok::Slider::resizeEvent( event ); 0325 The::amarokUrlHandler()->updateTimecodes(); 0326 } 0327 0328 void Amarok::TimeSlider::sliderChange( SliderChange change ) 0329 { 0330 if ( change == SliderValueChange || change == SliderRangeChange ) 0331 { 0332 int oldKnobX = m_knobX; 0333 qreal percent = 0.0; 0334 if ( maximum() > minimum() ) 0335 percent = ((qreal)value()) / ( maximum() - minimum() ); 0336 QRect knob = sliderHandleRect( rect(), percent ); 0337 m_knobX = knob.x(); 0338 0339 if (oldKnobX < m_knobX) 0340 update( oldKnobX, knob.y(), knob.right() + 1 - oldKnobX, knob.height() ); 0341 else if (oldKnobX > m_knobX) 0342 update( m_knobX, knob.y(), oldKnobX + knob.width(), knob.height() ); 0343 } 0344 else 0345 Amarok::Slider::sliderChange( change ); // calls update() 0346 } 0347 0348 void Amarok::TimeSlider::drawTriangle( const QString& name, int milliSeconds, bool showPopup ) 0349 { 0350 DEBUG_BLOCK 0351 int sliderHeight = height() - ( s_sliderInsertY * 2 ); 0352 int sliderLeftWidth = sliderHeight / 3; 0353 0354 // This mess converts the # of seconds into the pixel width value where the triangle should be drawn 0355 int x_pos = ( ( ( double ) milliSeconds - ( double ) minimum() ) / ( maximum() - minimum() ) ) * ( width() - ( sliderLeftWidth + sliderLeftWidth + s_sliderInsertX * 2 ) ); 0356 debug() << "drawing triangle at " << x_pos; 0357 BookmarkTriangle * tri = new BookmarkTriangle( this, milliSeconds, name, width(), showPopup ); 0358 connect( tri, &BookmarkTriangle::clicked, this, &TimeSlider::slotTriangleClicked ); 0359 connect( tri, &BookmarkTriangle::focused, this, &TimeSlider::slotTriangleFocused ); 0360 m_triangles << tri; 0361 tri->setGeometry( x_pos + 6 /* to center the point */, 1 /*y*/, 11, 11 ); // 6 = hard coded border width 0362 tri->show(); 0363 } 0364 0365 void Amarok::TimeSlider::slotTriangleClicked( int seconds ) 0366 { 0367 Q_EMIT sliderReleased( seconds ); 0368 } 0369 0370 void Amarok::TimeSlider::slotTriangleFocused( int seconds ) 0371 { 0372 QList<BookmarkTriangle *>::iterator i; 0373 for( i = m_triangles.begin(); i != m_triangles.end(); ++i ){ 0374 if( (*i)->getTimeValue() != seconds ) 0375 (*i)->hidePopup(); 0376 } 0377 } 0378 0379 void Amarok::TimeSlider::clearTriangles() 0380 { 0381 QList<BookmarkTriangle *>::iterator i; 0382 for( i = m_triangles.begin(); i != m_triangles.end(); ++i ){ 0383 (*i)->deleteLater(); 0384 } 0385 m_triangles.clear(); 0386 } 0387 0388 void Amarok::TimeSlider::mousePressEvent( QMouseEvent *event ) 0389 { 0390 if( !The::engineController()->isSeekable() ) 0391 return; // Eat the event,, it's not possible to seek 0392 Amarok::Slider::mousePressEvent( event ); 0393 } 0394 0395 bool Amarok::TimeSlider::event( QEvent * event ) 0396 { 0397 if( event->type() == QEvent::ToolTip ) 0398 { 0399 // Make a QHelpEvent out of this 0400 QHelpEvent * helpEvent = dynamic_cast<QHelpEvent *>( event ); 0401 if( helpEvent ) 0402 { 0403 0404 //figure out "percentage" of the track length that the mouse is hovering over the slider 0405 qreal percentage = (qreal) helpEvent->x() / (qreal) width(); 0406 long trackLength = The::engineController()->trackLength(); 0407 int trackPosition = trackLength * percentage; 0408 0409 // Update tooltip to show the track position under the cursor 0410 setToolTip( i18nc( "Tooltip shown when the mouse is over the progress slider, representing the position in the currently playing track that Amarok will seek to if you click the mouse. Keep it concise.", "Jump to: %1", Meta::msToPrettyTime( trackPosition ) ) ); 0411 } 0412 } 0413 0414 return QWidget::event( event ); 0415 } 0416 0417