Warning, file /multimedia/amarok/src/widgets/Osd.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) 2004 Christian Muehlhaeuser <chris@chris.de> * 0003 * Copyright (c) 2004-2006 Seb Ruiz <ruiz@kde.org> * 0004 * Copyright (c) 2004,2005 Max Howell <max.howell@methylblue.com> * 0005 * Copyright (c) 2005 Gabor Lehel <illissius@gmail.com> * 0006 * Copyright (c) 2008-2013 Mark Kretschmann <kretschmann@kde.org> * 0007 * * 0008 * This program is free software; you can redistribute it and/or modify it under * 0009 * the terms of the GNU General Public License as published by the Free Software * 0010 * Foundation; either version 2 of the License, or (at your option) any later * 0011 * version. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0014 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0015 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License along with * 0018 * this program. If not, see <http://www.gnu.org/licenses/>. * 0019 ****************************************************************************************/ 0020 0021 #define DEBUG_PREFIX "OSD" 0022 0023 #include "Osd.h" 0024 0025 #include "EngineController.h" 0026 #include "KNotificationBackend.h" 0027 #include "PaletteHandler.h" 0028 #include "SvgHandler.h" 0029 #include "amarokconfig.h" 0030 #include "core/meta/Meta.h" 0031 #include "core/meta/Statistics.h" 0032 #include "core/meta/support/MetaUtility.h" 0033 #include "core/support/Amarok.h" 0034 #include "core/support/Debug.h" 0035 #include "widgets/StarManager.h" 0036 0037 #include <QApplication> 0038 #include <QIcon> 0039 #include <KLocalizedString> 0040 #include <KWindowSystem> 0041 #include <KIconLoader> 0042 0043 #include <QDesktopWidget> 0044 #include <QMouseEvent> 0045 #include <QPainter> 0046 #include <QPixmap> 0047 #include <QRegExp> 0048 #include <QTimeLine> 0049 #include <QTimer> 0050 0051 namespace ShadowEngine 0052 { 0053 QImage makeShadow( const QPixmap &textPixmap, const QColor &bgColor ); 0054 } 0055 0056 namespace Amarok 0057 { 0058 inline QImage icon() { return QImage( KIconLoader::global()->iconPath( "amarok", -KIconLoader::SizeHuge ) ); } 0059 } 0060 0061 OSDWidget::OSDWidget( QWidget *parent, const char *name ) 0062 : QWidget( parent ) 0063 , m_duration( 2000 ) 0064 , m_timer( new QTimer( this ) ) 0065 , m_alignment( Middle ) 0066 , m_screen( 0 ) 0067 , m_yOffset( MARGIN ) 0068 , m_rating( 0 ) 0069 , m_volume( The::engineController()->volume() ) 0070 , m_showVolume( false ) 0071 , m_hideWhenFullscreenWindowIsActive( false ) 0072 , m_fadeTimeLine( new QTimeLine( FADING_DURATION, this ) ) 0073 { 0074 Qt::WindowFlags flags; 0075 flags = Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint; 0076 // The best of both worlds. On Windows, setting the widget as a popup avoids a task manager entry. On linux, a popup steals focus. 0077 // Therefore we go need to do it platform specific :( 0078 0079 //This is no longer true. Qt::Window steals focus on X11, Qt:Tool does not. Not sure if we even need the ifdefs any more... 0080 #ifdef Q_OS_WIN 0081 flags |= Qt::Tool; 0082 #else 0083 flags |= Qt::Tool | Qt::X11BypassWindowManagerHint; 0084 #endif 0085 setWindowFlags( flags ); 0086 setObjectName( name ); 0087 setFocusPolicy( Qt::NoFocus ); 0088 0089 #ifdef Q_WS_X11 0090 KWindowSystem::setType( winId(), NET::Notification ); 0091 #endif 0092 0093 m_timer->setSingleShot( true ); 0094 connect( m_timer, &QTimer::timeout, this, &OSDWidget::hide ); 0095 0096 m_fadeTimeLine->setUpdateInterval( 30 ); //~33 frames per second 0097 connect( m_fadeTimeLine, &QTimeLine::valueChanged, this, &OSDWidget::setFadeOpacity ); 0098 0099 //or crashes, KWindowSystem bug I think, crashes in QWidget::icon() 0100 //kapp->setTopWidget( this ); 0101 } 0102 0103 OSDWidget::~OSDWidget() 0104 { 0105 DEBUG_BLOCK 0106 } 0107 0108 void 0109 OSDWidget::show( const QString &text, const QImage &newImage ) 0110 { 0111 DEBUG_BLOCK 0112 m_showVolume = false; 0113 if ( !newImage.isNull() ) 0114 { 0115 m_cover = newImage; 0116 int w = m_scaledCover.width(); 0117 int h = m_scaledCover.height(); 0118 m_scaledCover = QPixmap::fromImage( m_cover.scaled( w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) ); 0119 } 0120 else 0121 m_cover = Amarok::icon(); 0122 0123 m_text = text; 0124 show(); 0125 } 0126 0127 void 0128 OSDWidget::show() 0129 { 0130 if ( !isTemporaryDisabled() ) 0131 { 0132 QWidget::show(); 0133 0134 if( windowOpacity() == 0.0 && KWindowSystem::compositingActive() ) 0135 { 0136 m_fadeTimeLine->setDirection( QTimeLine::Forward ); 0137 m_fadeTimeLine->start(); 0138 } 0139 // Skip fading if OSD is already visible or if compositing is disabled 0140 else 0141 { 0142 m_fadeTimeLine->stop(); 0143 setWindowOpacity( maxOpacity() ); 0144 } 0145 } 0146 } 0147 0148 void 0149 OSDWidget::hide() 0150 { 0151 if( KWindowSystem::compositingActive() ) 0152 { 0153 m_fadeTimeLine->setDirection( QTimeLine::Backward ); 0154 m_fadeTimeLine->start(); 0155 } 0156 else 0157 { 0158 QWidget::hide(); 0159 } 0160 } 0161 0162 bool 0163 OSDWidget::isTemporaryDisabled() const 0164 { 0165 // Check if the OSD should not be shown, 0166 // if a fullscreen window is focused. 0167 if ( m_hideWhenFullscreenWindowIsActive ) 0168 { 0169 return Amarok::KNotificationBackend::instance()->isFullscreenWindowActive(); 0170 } 0171 0172 return false; 0173 } 0174 0175 void 0176 OSDWidget::ratingChanged( const QString& path, int rating ) 0177 { 0178 Meta::TrackPtr track = The::engineController()->currentTrack(); 0179 if( !track ) 0180 return; 0181 if( track->playableUrl().isLocalFile() && track->playableUrl().path() == path ) 0182 ratingChanged( rating ); 0183 } 0184 0185 void 0186 OSDWidget::ratingChanged( const short rating ) 0187 { 0188 m_text = QLatin1Char('\n') + i18n( "Rating changed" ); 0189 setRating( rating ); //Checks isEnabled() before doing anything 0190 0191 show(); 0192 } 0193 0194 void 0195 OSDWidget::volumeChanged( int volume ) 0196 { 0197 m_volume = volume; 0198 0199 if ( isEnabled() ) 0200 { 0201 m_showVolume = true; 0202 m_text = The::engineController()->isMuted() ? i18n( "Volume: %1% (muted)", m_volume) : i18n( "Volume: %1%", m_volume); 0203 0204 show(); 0205 } 0206 } 0207 0208 void 0209 OSDWidget::setVisible( bool visible ) 0210 { 0211 if ( visible ) 0212 { 0213 if ( !isEnabled() || m_text.isEmpty() ) 0214 return; 0215 0216 const uint margin = fontMetrics().horizontalAdvance( 'x' ); 0217 0218 const QRect newGeometry = determineMetrics( margin ); 0219 0220 if( newGeometry.width() > 0 && newGeometry.height() > 0 ) 0221 { 0222 m_margin = margin; 0223 m_size = newGeometry.size(); 0224 setGeometry( newGeometry ); 0225 QWidget::setVisible( visible ); 0226 0227 if( m_duration ) //duration 0 -> stay forever 0228 m_timer->start( m_duration ); //calls hide() 0229 } 0230 else 0231 warning() << "Attempted to make an invalid sized OSD\n"; 0232 0233 update(); 0234 } 0235 else 0236 QWidget::setVisible( visible ); 0237 } 0238 0239 QRect 0240 OSDWidget::determineMetrics( const int M ) 0241 { 0242 // sometimes we only have a tiddly cover 0243 const QSize minImageSize = m_cover.size().boundedTo( QSize( 100, 100 ) ); 0244 0245 // determine a sensible maximum size, don't cover the whole desktop or cross the screen 0246 const QSize margin( ( M + MARGIN ) * 2, ( M + MARGIN ) * 2 ); //margins 0247 const QSize image = m_cover.isNull() ? QSize( 0, 0 ) : minImageSize; 0248 const QSize max = QApplication::desktop()->screen( m_screen )->size() - margin; 0249 0250 // If we don't do that, the boundingRect() might not be suitable for drawText() (Qt issue N67674) 0251 m_text.replace( QRegExp( " +\n" ), "\n" ); 0252 // remove consecutive line breaks 0253 m_text.replace( QRegExp( "\n+" ), "\n" ); 0254 0255 // The osd cannot be larger than the screen 0256 QRect rect = fontMetrics().boundingRect( 0, 0, max.width() - image.width(), max.height(), 0257 Qt::AlignCenter, m_text ); 0258 rect.adjust( 0, 0, SHADOW_SIZE * 2, SHADOW_SIZE * 2 ); // the shadow needs some space 0259 0260 if( m_showVolume ) 0261 { 0262 static const QString tmp = QString ("******").insert( 3, 0263 ( i18n("Volume: 100% (muted)" ) ) ); 0264 0265 QRect tmpRect = fontMetrics().boundingRect( 0, 0, 0266 max.width() - image.width(), max.height() - fontMetrics().height(), 0267 Qt::AlignCenter, tmp ); 0268 tmpRect.setHeight( tmpRect.height() + fontMetrics().height() / 2 ); 0269 0270 rect = tmpRect; 0271 0272 if ( The::engineController()->isMuted() ) 0273 m_cover = The::svgHandler()->renderSvg( "Muted", 100, 100, "Muted" ).toImage(); 0274 else if( m_volume > 66 ) 0275 m_cover = The::svgHandler()->renderSvg( "Volume", 100, 100, "Volume" ).toImage(); 0276 else if ( m_volume > 33 ) 0277 m_cover = The::svgHandler()->renderSvg( "Volume_mid", 100, 100, "Volume_mid" ).toImage(); 0278 else 0279 m_cover = The::svgHandler()->renderSvg( "Volume_low", 100, 100, "Volume_low" ).toImage(); 0280 } 0281 // Don't show both volume and rating 0282 else if( m_rating ) 0283 { 0284 QPixmap* star = StarManager::instance()->getStar( 1 ); 0285 if( rect.width() < star->width() * 5 ) 0286 rect.setWidth( star->width() * 5 ); //changes right edge position 0287 rect.setHeight( rect.height() + star->height() + M ); //changes bottom edge pos 0288 } 0289 0290 if( !m_cover.isNull() ) 0291 { 0292 const int availableWidth = max.width() - rect.width() - M; //WILL be >= (minImageSize.width() - M) 0293 0294 m_scaledCover = QPixmap::fromImage( 0295 m_cover.scaled( 0296 qMin( availableWidth, m_cover.width() ), 0297 qMin( rect.height(), m_cover.height() ), 0298 Qt::KeepAspectRatio, Qt::SmoothTransformation 0299 ) 0300 ); //this will force us to be with our bounds 0301 0302 0303 const int widthIncludingImage = rect.width() 0304 + m_scaledCover.width() 0305 + M; //margin between text + image 0306 0307 rect.setWidth( widthIncludingImage ); 0308 } 0309 0310 // expand in all directions by M 0311 rect.adjust( -M, -M, M, M ); 0312 0313 const QSize newSize = rect.size(); 0314 const QRect screen = QApplication::desktop()->screenGeometry( m_screen ); 0315 QPoint newPos( MARGIN, m_yOffset ); 0316 0317 switch( m_alignment ) 0318 { 0319 case Left: 0320 break; 0321 0322 case Right: 0323 newPos.rx() = screen.width() - MARGIN - newSize.width(); 0324 break; 0325 0326 case Center: 0327 newPos.ry() = ( screen.height() - newSize.height() ) / 2; 0328 0329 Q_FALLTHROUGH(); 0330 0331 case Middle: 0332 newPos.rx() = ( screen.width() - newSize.width() ) / 2; 0333 break; 0334 } 0335 0336 //ensure we don't dip below the screen 0337 if ( newPos.y() + newSize.height() > screen.height() - MARGIN ) 0338 newPos.ry() = screen.height() - MARGIN - newSize.height(); 0339 0340 // correct for screen position 0341 newPos += screen.topLeft(); 0342 0343 return QRect( newPos, rect.size() ); 0344 } 0345 0346 void 0347 OSDWidget::paintEvent( QPaintEvent *e ) 0348 { 0349 QRect rect( QPoint(), m_size ); 0350 0351 QColor shadowColor; 0352 { 0353 int h, s, v; 0354 palette().color( QPalette::Normal, QPalette::WindowText ).getHsv( &h, &s, &v ); 0355 shadowColor = v > 128 ? Qt::black : Qt::white; 0356 } 0357 0358 const int align = Qt::AlignCenter; 0359 0360 QPainter p( this ); 0361 p.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing ); 0362 p.setClipRect( e->rect() ); 0363 0364 QPixmap background = The::svgHandler()->renderSvgWithDividers( "service_list_item", width(), height(), "service_list_item" ); 0365 p.drawPixmap( 0, 0, background ); 0366 0367 //p.setPen( Qt::white ); // Revert this when the background can be colorized again. 0368 rect.adjust( m_margin, m_margin, -m_margin, -m_margin ); // subtract margins 0369 0370 if( !m_cover.isNull() ) 0371 { 0372 QRect r( rect ); 0373 r.setTop( ( m_size.height() - m_scaledCover.height() ) / 2 ); 0374 r.setSize( m_scaledCover.size() ); 0375 0376 p.drawPixmap( r.topLeft(), m_scaledCover ); 0377 0378 rect.setLeft( rect.left() + m_scaledCover.width() + m_margin ); 0379 } 0380 0381 int graphicsHeight = 0; 0382 0383 if( !m_showVolume && m_rating > 0 && !m_paused ) 0384 { 0385 // TODO: Check if we couldn't use a KRatingPainter instead 0386 QPixmap* star = StarManager::instance()->getStar( m_rating/2 ); 0387 QRect r( rect ); 0388 0389 //Align to center... 0390 r.setLeft( ( rect.left() + rect.width() / 2 ) - star->width() * m_rating / 4 ); 0391 r.setTop( rect.bottom() - star->height() ); 0392 graphicsHeight += star->height() + m_margin; 0393 0394 const bool half = m_rating % 2; 0395 0396 if( half ) 0397 { 0398 QPixmap* halfStar = StarManager::instance()->getHalfStar( m_rating / 2 + 1 ); 0399 p.drawPixmap( r.left() + star->width() * ( m_rating / 2 ), r.top(), *halfStar ); 0400 star = StarManager::instance()->getStar( m_rating / 2 + 1 ); 0401 } 0402 0403 for( int i = 0; i < m_rating / 2; i++ ) 0404 { 0405 p.drawPixmap( r.left() + i * star->width(), r.top(), *star ); 0406 } 0407 } 0408 0409 rect.setBottom( rect.bottom() - graphicsHeight ); 0410 0411 // Draw "shadow" text effect (black outline) (currently it's up to five pixel in every dir.) 0412 QPixmap pixmap( rect.size() ); 0413 pixmap.fill( Qt::black ); 0414 0415 QPainter p2( &pixmap ); 0416 p2.setFont( font() ); 0417 p2.setPen( Qt::white ); 0418 p2.setBrush( Qt::white ); 0419 p2.drawText( QRect( QPoint( SHADOW_SIZE, SHADOW_SIZE ), 0420 QSize( rect.size().width() - SHADOW_SIZE * 2, 0421 rect.size().height() - SHADOW_SIZE * 2 ) ), 0422 align, m_text ); 0423 p2.end(); 0424 0425 p.drawImage( rect.topLeft(), ShadowEngine::makeShadow( pixmap, shadowColor ) ); 0426 0427 p.setPen( palette().color( QPalette::Active, QPalette::WindowText ) ); 0428 0429 p.drawText( rect.adjusted( SHADOW_SIZE, SHADOW_SIZE, 0430 -SHADOW_SIZE, -SHADOW_SIZE ), align, m_text ); 0431 } 0432 0433 void 0434 OSDWidget::changeEvent( QEvent *event ) 0435 { 0436 QWidget::changeEvent( event ); 0437 0438 if( event->type() == QEvent::PaletteChange ) 0439 if( !AmarokConfig::osdUseCustomColors() ) 0440 unsetColors(); // Use new palette's colors 0441 } 0442 0443 void 0444 OSDWidget::mousePressEvent( QMouseEvent* ) 0445 { 0446 hide(); 0447 } 0448 0449 void 0450 OSDWidget::unsetColors() 0451 { 0452 setPalette( The::paletteHandler()->palette() ); 0453 } 0454 0455 void 0456 OSDWidget::setTextColor(const QColor& color) 0457 { 0458 QPalette palette = this->palette(); 0459 palette.setColor( QPalette::Active, QPalette::WindowText, color ); 0460 setPalette(palette); 0461 } 0462 0463 void 0464 OSDWidget::setScreen( int screen ) 0465 { 0466 const int n = QApplication::desktop()->numScreens(); 0467 m_screen = ( screen >= n ) ? n - 1 : screen; 0468 } 0469 0470 void 0471 OSDWidget::setFadeOpacity( qreal value ) 0472 { 0473 setWindowOpacity( value * maxOpacity() ); 0474 0475 if( value == 0.0 ) 0476 { 0477 QWidget::hide(); 0478 } 0479 } 0480 0481 void 0482 OSDWidget::setFontScale( int scale ) 0483 { 0484 double fontScale = static_cast<double>( scale ) / 100.0; 0485 0486 // update font, reuse old one 0487 QFont newFont( font() ); 0488 newFont.setPointSizeF( defaultPointSize() * fontScale ); 0489 setFont( newFont ); 0490 } 0491 0492 void 0493 OSDWidget::setHideWhenFullscreenWindowIsActive( bool hide ) 0494 { 0495 m_hideWhenFullscreenWindowIsActive = hide; 0496 } 0497 0498 0499 ///////////////////////////////////////////////////////////////////////////////////////// 0500 // Class OSDPreviewWidget 0501 ///////////////////////////////////////////////////////////////////////////////////////// 0502 0503 OSDPreviewWidget::OSDPreviewWidget( QWidget *parent ) 0504 : OSDWidget( parent ) 0505 , m_dragging( false ) 0506 { 0507 setObjectName( "osdpreview" ); 0508 setDuration( 0 ); 0509 setImage( Amarok::icon() ); 0510 setTranslucent( AmarokConfig::osdUseTranslucency() ); 0511 setText( i18n( "On-Screen-Display preview\nDrag to reposition" ) ); 0512 } 0513 0514 void 0515 OSDPreviewWidget::mousePressEvent( QMouseEvent *event ) 0516 { 0517 m_dragYOffset = event->pos(); 0518 0519 if( event->button() == Qt::LeftButton && !m_dragging ) 0520 { 0521 grabMouse( Qt::SizeAllCursor ); 0522 m_dragging = true; 0523 } 0524 } 0525 0526 void 0527 OSDPreviewWidget::setUseCustomColors(const bool use, const QColor& fg) 0528 { 0529 if( use ) 0530 setTextColor( fg ); 0531 else 0532 unsetColors(); 0533 } 0534 0535 void 0536 OSDPreviewWidget::mouseReleaseEvent( QMouseEvent * /*event*/ ) 0537 { 0538 if( m_dragging ) 0539 { 0540 m_dragging = false; 0541 releaseMouse(); 0542 0543 Q_EMIT positionChanged(); 0544 } 0545 } 0546 0547 void 0548 OSDPreviewWidget::mouseMoveEvent( QMouseEvent *e ) 0549 { 0550 if( m_dragging && this == mouseGrabber() ) 0551 { 0552 // Here we implement a "snap-to-grid" like positioning system for the preview widget 0553 0554 const QRect screenRect = QApplication::desktop()->screenGeometry( screen() ); 0555 const uint hcenter = screenRect.width() / 2; 0556 const uint eGlobalPosX = e->globalPos().x() - screenRect.left(); 0557 const uint snapZone = screenRect.width() / 24; 0558 0559 QPoint destination = e->globalPos() - m_dragYOffset - screenRect.topLeft(); 0560 int maxY = screenRect.height() - height() - MARGIN; 0561 if( destination.y() < MARGIN ) 0562 destination.ry() = MARGIN; 0563 if( destination.y() > maxY ) 0564 destination.ry() = maxY; 0565 0566 if( eGlobalPosX < ( hcenter - snapZone ) ) 0567 { 0568 setAlignment(Left); 0569 destination.rx() = MARGIN; 0570 } 0571 else if( eGlobalPosX > ( hcenter + snapZone ) ) 0572 { 0573 setAlignment(Right); 0574 destination.rx() = screenRect.width() - MARGIN - width(); 0575 } 0576 else { 0577 const uint eGlobalPosY = e->globalPos().y() - screenRect.top(); 0578 const uint vcenter = screenRect.height() / 2; 0579 0580 destination.rx() = hcenter - width() / 2; 0581 0582 if( eGlobalPosY >= ( vcenter - snapZone ) && eGlobalPosY <= ( vcenter + snapZone ) ) 0583 { 0584 setAlignment(Center); 0585 destination.ry() = vcenter - height() / 2; 0586 } 0587 else 0588 setAlignment(Middle); 0589 } 0590 0591 destination += screenRect.topLeft(); 0592 move( destination ); 0593 0594 // compute current Position && Y-offset 0595 QDesktopWidget *desktop = QApplication::desktop(); 0596 const int currentScreen = desktop->screenNumber( pos() ); 0597 0598 // set new data 0599 OSDWidget::setScreen( currentScreen ); 0600 setYOffset( y() ); 0601 } 0602 } 0603 0604 0605 ///////////////////////////////////////////////////////////////////////////////////////// 0606 // Class OSD 0607 ///////////////////////////////////////////////////////////////////////////////////////// 0608 0609 Amarok::OSD* Amarok::OSD::s_instance = nullptr; 0610 0611 Amarok::OSD* 0612 Amarok::OSD::instance() 0613 { 0614 return s_instance ? s_instance : new OSD(); 0615 } 0616 0617 void 0618 Amarok::OSD::destroy() 0619 { 0620 if ( s_instance ) 0621 { 0622 delete s_instance; 0623 s_instance = nullptr; 0624 } 0625 } 0626 0627 Amarok::OSD::OSD() 0628 : OSDWidget( nullptr ) 0629 { 0630 s_instance = this; 0631 0632 EngineController* const engine = The::engineController(); 0633 0634 if( engine->isPlaying() ) 0635 trackPlaying( engine->currentTrack() ); 0636 0637 connect( engine, &EngineController::trackPlaying, 0638 this, &Amarok::OSD::trackPlaying ); 0639 connect( engine, &EngineController::stopped, 0640 this, &Amarok::OSD::stopped ); 0641 connect( engine, &EngineController::paused, 0642 this, &Amarok::OSD::paused ); 0643 0644 connect( engine, &EngineController::trackMetadataChanged, 0645 this, &Amarok::OSD::metadataChanged ); 0646 connect( engine, &EngineController::albumMetadataChanged, 0647 this, &Amarok::OSD::metadataChanged ); 0648 0649 connect( engine, &EngineController::volumeChanged, 0650 this, &Amarok::OSD::volumeChanged ); 0651 0652 connect( engine, &EngineController::muteStateChanged, 0653 this, &Amarok::OSD::muteStateChanged ); 0654 0655 } 0656 0657 Amarok::OSD::~OSD() 0658 {} 0659 0660 void 0661 Amarok::OSD::show( Meta::TrackPtr track ) //slot 0662 { 0663 setAlignment( static_cast<OSDWidget::Alignment>( AmarokConfig::osdAlignment() ) ); 0664 setYOffset( AmarokConfig::osdYOffset() ); 0665 0666 QString text; 0667 if( !track || track->playableUrl().isEmpty() ) 0668 { 0669 text = i18n( "No track playing" ); 0670 setRating( 0 ); // otherwise stars from last rating change are visible 0671 } 0672 else 0673 { 0674 setRating( track->statistics()->rating() ); 0675 text = track->prettyName(); 0676 if( track->artist() && !track->artist()->prettyName().isEmpty() ) 0677 text = track->artist()->prettyName() + " - " + text; 0678 if( track->album() && !track->album()->prettyName().isEmpty() ) 0679 text += "\n (" + track->album()->prettyName() + ") "; 0680 else 0681 text += '\n'; 0682 if( track->length() > 0 ) 0683 text += Meta::msToPrettyTime( track->length() ); 0684 } 0685 0686 if( text.isEmpty() ) 0687 text = track->playableUrl().fileName(); 0688 0689 if( text.startsWith( "- " ) ) //When we only have a title tag, _something_ prepends a fucking hyphen. Remove that. 0690 text = text.mid( 2 ); 0691 0692 if( text.isEmpty() ) //still 0693 text = i18n("No information available for this track"); 0694 0695 QImage image; 0696 if( track && track->album() ) 0697 image = The::svgHandler()->imageWithBorder( track->album(), 100, 5 ).toImage(); 0698 0699 OSDWidget::show( text, image ); 0700 } 0701 0702 void 0703 Amarok::OSD::applySettings() 0704 { 0705 DEBUG_BLOCK 0706 0707 setAlignment( static_cast<OSDWidget::Alignment>( AmarokConfig::osdAlignment() ) ); 0708 setDuration( AmarokConfig::osdDuration() ); 0709 setEnabled( AmarokConfig::osdEnabled() ); 0710 setYOffset( AmarokConfig::osdYOffset() ); 0711 setScreen( AmarokConfig::osdScreen() ); 0712 setFontScale( AmarokConfig::osdFontScaling() ); 0713 setHideWhenFullscreenWindowIsActive( AmarokConfig::osdHideOnFullscreen() ); 0714 0715 if( AmarokConfig::osdUseCustomColors() ) 0716 setTextColor( AmarokConfig::osdTextColor() ); 0717 else 0718 unsetColors(); 0719 0720 setTranslucent( AmarokConfig::osdUseTranslucency() ); 0721 } 0722 0723 void 0724 Amarok::OSD::forceToggleOSD() 0725 { 0726 if ( !isVisible() ) 0727 { 0728 const bool b = isEnabled(); 0729 setEnabled( true ); 0730 show( The::engineController()->currentTrack() ); 0731 setEnabled( b ); 0732 } 0733 else 0734 hide(); 0735 } 0736 0737 void 0738 Amarok::OSD::muteStateChanged( bool mute ) 0739 { 0740 Q_UNUSED( mute ) 0741 0742 volumeChanged( The::engineController()->volume() ); 0743 } 0744 0745 void 0746 Amarok::OSD::trackPlaying( const Meta::TrackPtr &track ) 0747 { 0748 m_currentTrack = track; 0749 0750 setPaused(false); 0751 show( m_currentTrack ); 0752 } 0753 0754 void 0755 Amarok::OSD::stopped() 0756 { 0757 setImage( QImage( KIconLoader::global()->iconPath( "amarok", -KIconLoader::SizeHuge ) ) ); 0758 setRating( 0 ); // otherwise stars from last rating change are visible 0759 OSDWidget::show( i18n( "Stopped" ) ); 0760 setPaused(false); 0761 } 0762 0763 void 0764 Amarok::OSD::paused() 0765 { 0766 setImage( QImage( KIconLoader::global()->iconPath( "amarok", -KIconLoader::SizeHuge ) ) ); 0767 setRating( 0 ); // otherwise stars from last rating change are visible 0768 OSDWidget::show( i18n( "Paused" ) ); 0769 setPaused(true); 0770 } 0771 0772 void 0773 Amarok::OSD::metadataChanged() 0774 { 0775 // this also covers all cases where a stream get's new metadata. 0776 show( m_currentTrack ); 0777 } 0778 0779 0780 /* Code copied from kshadowengine.cpp 0781 * 0782 * Copyright (C) 2003 Laur Ivan <laurivan@eircom.net> 0783 * 0784 * Many thanks to: 0785 * - Bernardo Hung <deciare@gta.igs.net> for the enhanced shadow 0786 * algorithm (currently used) 0787 * - Tim Jansen <tim@tjansen.de> for the API updates and fixes. 0788 * 0789 * This library is free software; you can redistribute it and/or 0790 * modify it under the terms of the GNU Library General Public 0791 * License version 2 as published by the Free Software Foundation. 0792 * 0793 * This library is distributed in the hope that it will be useful, 0794 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0795 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0796 * Library General Public License for more details. 0797 * 0798 * You should have received a copy of the GNU Library General Public License 0799 * along with this library; see the file COPYING.LIB. If not, write to 0800 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0801 * Boston, MA 02110-1301, USA. 0802 */ 0803 0804 namespace ShadowEngine 0805 { 0806 // Not sure, doesn't work above 10 0807 static const int MULTIPLICATION_FACTOR = 3; 0808 // Multiplication factor for pixels directly above, under, or next to the text 0809 static const double AXIS_FACTOR = 2.0; 0810 // Multiplication factor for pixels diagonal to the text 0811 static const double DIAGONAL_FACTOR = 0.1; 0812 // Self explanatory 0813 static const int MAX_OPACITY = 200; 0814 0815 double decay( QImage&, int, int ); 0816 0817 QImage makeShadow( const QPixmap& textPixmap, const QColor &bgColor ) 0818 { 0819 const int w = textPixmap.width(); 0820 const int h = textPixmap.height(); 0821 const int bgr = bgColor.red(); 0822 const int bgg = bgColor.green(); 0823 const int bgb = bgColor.blue(); 0824 0825 int alphaShadow; 0826 0827 // This is the source pixmap 0828 QImage img = textPixmap.toImage(); 0829 0830 QImage result( w, h, QImage::Format_ARGB32 ); 0831 result.fill( 0 ); // fill with black 0832 0833 static const int M = OSDWidget::SHADOW_SIZE; 0834 for( int i = M; i < w - M; i++) { 0835 for( int j = M; j < h - M; j++ ) 0836 { 0837 alphaShadow = (int) decay( img, i, j ); 0838 0839 result.setPixel( i,j, qRgba( bgr, bgg , bgb, qMin( MAX_OPACITY, alphaShadow ) ) ); 0840 } 0841 } 0842 0843 return result; 0844 } 0845 0846 double decay( QImage& source, int i, int j ) 0847 { 0848 //if ((i < 1) || (j < 1) || (i > source.width() - 2) || (j > source.height() - 2)) 0849 // return 0; 0850 0851 double alphaShadow; 0852 alphaShadow =(qGray(source.pixel(i-1,j-1)) * DIAGONAL_FACTOR + 0853 qGray(source.pixel(i-1,j )) * AXIS_FACTOR + 0854 qGray(source.pixel(i-1,j+1)) * DIAGONAL_FACTOR + 0855 qGray(source.pixel(i ,j-1)) * AXIS_FACTOR + 0856 0 + 0857 qGray(source.pixel(i ,j+1)) * AXIS_FACTOR + 0858 qGray(source.pixel(i+1,j-1)) * DIAGONAL_FACTOR + 0859 qGray(source.pixel(i+1,j )) * AXIS_FACTOR + 0860 qGray(source.pixel(i+1,j+1)) * DIAGONAL_FACTOR) / MULTIPLICATION_FACTOR; 0861 0862 return alphaShadow; 0863 } 0864 } 0865