📄 osd.cpp
字号:
/* * 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. * * osd.cpp: Shows some text in a pretty way independent to the WM * begin: Fre Sep 26 2003 * copyright: (C) 2004 Christian Muehlhaeuser <chris@chris.de> * (C) 2004-2006 Seb Ruiz <me@sebruiz.net> * (C) 2004, 2005 Max Howell * (C) 2005 Gábor Lehel <illissius@gmail.com> */#include "amarok.h"#include "amarokconfig.h"#include "collectiondb.h" //for albumCover location#include "debug.h"#include "enginecontroller.h"#include "osd.h"#include "playlist.h" //if osdUsePlaylistColumns()#include "playlistitem.h" //ditto#include "podcastbundle.h"#include "qstringx.h"#include "starmanager.h"#include <kapplication.h>#include <kpixmap.h>#include <kpixmapeffect.h>#include <kstandarddirs.h> //locate#include <qbitmap.h>#include <qpainter.h>#include <qpixmap.h>#include <qregexp.h>#include <qtimer.h>#include <qvaluevector.h>namespace ShadowEngine{ QImage makeShadow( const QPixmap &textPixmap, const QColor &bgColor );}#define MOODBAR_HEIGHT 20OSDWidget::OSDWidget( QWidget *parent, const char *name ) : QWidget( parent, name, WType_TopLevel | WNoAutoErase | WStyle_Customize | WX11BypassWM | WStyle_StaysOnTop ) , m_duration( 2000 ) , m_timer( new QTimer( this ) ) , m_alignment( Middle ) , m_screen( 0 ) , m_y( MARGIN ) , m_drawShadow( false ) , m_translucency( false ) , m_rating( 0 ) , m_volume( false ){ setFocusPolicy( NoFocus ); setBackgroundMode( NoBackground ); unsetColors(); connect( m_timer, SIGNAL(timeout()), SLOT(hide()) ); connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString&, int ) ), this, SLOT( ratingChanged( const QString&, int ) ) ); //or crashes, KWin bug I think, crashes in QWidget::icon() kapp->setTopWidget( this );}voidOSDWidget::show( const QString &text, QImage newImage ){#ifdef Q_WS_X11 m_text = text; if ( !newImage.isNull() ) { m_cover = newImage; int w = m_scaledCover.width(); int h = m_scaledCover.height(); m_scaledCover = m_cover.smoothScale(w, h); } show();#else Q_UNUSED( text ); Q_UNUSED( newImage );#endif}voidOSDWidget::ratingChanged( const short rating ){ //m_text = '\n' + i18n( "Rating changed" ); setRating( rating ); //Checks isEnabled() before doing anything if( useMoodbar() ) OSDWidget::setMoodbar( EngineController::instance()->bundle() ); if( isShown() ) show();}voidOSDWidget::ratingChanged( const QString& path, int rating ){ const MetaBundle ¤tTrack = EngineController::instance()->bundle(); if( currentTrack.isFile() && currentTrack.url().path() == path ) ratingChanged( rating );}voidOSDWidget::volChanged( unsigned char volume ){ if ( isEnabled() ) { m_volume = true; m_newvolume = volume; m_text = m_newvolume ? i18n("Volume: %1%").arg( m_newvolume ) : i18n("Mute"); show(); }}voidOSDWidget::show() //virtual{#ifdef Q_WS_X11 if ( !isEnabled() || m_text.isEmpty() ) return; const uint M = fontMetrics().width( 'x' ); const QRect oldGeometry = QRect( pos(), size() ); const QRect newGeometry = determineMetrics( M ); if( m_translucency && !isShown() || !newGeometry.intersects( oldGeometry ) ) m_screenshot = QPixmap::grabWindow( qt_xrootwin(), newGeometry.x(), newGeometry.y(), newGeometry.width(), newGeometry.height() ); else if( m_translucency ) { const QRect unite = oldGeometry.unite( newGeometry ); KPixmap pix = QPixmap::grabWindow( qt_xrootwin(), unite.x(), unite.y(), unite.width(), unite.height() ); QPoint p = oldGeometry.topLeft() - unite.topLeft(); bitBlt( &pix, p, &m_screenshot ); m_screenshot.resize( newGeometry.size() ); p = newGeometry.topLeft() - unite.topLeft(); bitBlt( &m_screenshot, 0, 0, &pix, p.x(), p.y() ); } if( newGeometry.width() > 0 && newGeometry.height() > 0 ) { render( M, newGeometry.size() ); setGeometry( newGeometry ); QWidget::show(); bitBlt( this, 0, 0, &m_buffer ); if( m_duration ) //duration 0 -> stay forever m_timer->start( m_duration, true ); //calls hide() } else warning() << "Attempted to make an invalid sized OSD\n";#endif}QRectOSDWidget::determineMetrics( const uint M ){ // sometimes we only have a tiddly cover const QSize minImageSize = m_cover.size().boundedTo( QSize(100,100) ); // determine a sensible maximum size, don't cover the whole desktop or cross the screen const QSize margin( (M + MARGIN) * 2, (M + MARGIN) * 2 ); //margins const QSize image = m_cover.isNull() ? QSize( 0, 0 ) : minImageSize; const QSize max = QApplication::desktop()->screen( m_screen )->size() - margin; // If we don't do that, the boundingRect() might not be suitable for drawText() (Qt issue N67674) m_text.replace( QRegExp(" +\n"), "\n" ); // remove consecutive line breaks m_text.replace( QRegExp("\n+"), "\n" ); // The osd cannot be larger than the screen QRect rect = fontMetrics().boundingRect( 0, 0, max.width() - image.width(), max.height(), AlignCenter | WordBreak, m_text ); if( m_volume ) { static const QString tmp = QString ("******").insert( 3, ( i18n("Volume: 100%").length() >= i18n("Mute").length() )? i18n("Volume: 100%") : i18n("Mute") ); QRect tmpRect = fontMetrics().boundingRect( 0, 0, max.width() - image.width(), max.height() - fontMetrics().height(), AlignCenter | WordBreak, tmp ); tmpRect.setHeight( tmpRect.height() + fontMetrics().height() / 2 ); rect = tmpRect; } if( m_rating ) { QPixmap* star = StarManager::instance()->getStar( 1, true ); if( rect.width() < star->width() * 5 ) rect.setWidth( star->width() * 5 ); //changes right edge position rect.setHeight( rect.height() + star->height() + M ); //changes bottom edge pos } if( useMoodbar() ) rect.setHeight( rect.height() + MOODBAR_HEIGHT + M ); if( !m_cover.isNull() ) { const int availableWidth = max.width() - rect.width() - M; //WILL be >= (minImageSize.width() - M) m_scaledCover = m_cover.smoothScale( QMIN( availableWidth, m_cover.width() ), QMIN( rect.height(), m_cover.height() ), QImage::ScaleMin ); //this will force us to be with our bounds int shadowWidth = 0; if( m_drawShadow && !m_scaledCover.hasAlpha() && ( m_scaledCover.width() > 22 || m_scaledCover.height() > 22 ) ) shadowWidth = static_cast<uint>( m_scaledCover.width() / 100.0 * 6.0 ); const int widthIncludingImage = rect.width() + m_scaledCover.width() + shadowWidth + M; //margin between text + image rect.setWidth( widthIncludingImage ); } // expand in all directions by M rect.addCoords( -M, -M, M, M ); const QSize newSize = rect.size(); const QRect screen = QApplication::desktop()->screenGeometry( m_screen ); QPoint newPos( MARGIN, m_y ); switch( m_alignment ) { case Left: break; case Right: newPos.rx() = screen.width() - MARGIN - newSize.width(); break; case Center: newPos.ry() = (screen.height() - newSize.height()) / 2; //FALL THROUGH case Middle: newPos.rx() = (screen.width() - newSize.width()) / 2; break; } //ensure we don't dip below the screen if ( newPos.y() + newSize.height() > screen.height() - MARGIN ) newPos.ry() = screen.height() - MARGIN - newSize.height(); // correct for screen position newPos += screen.topLeft(); return QRect( newPos, rect.size() );}voidOSDWidget::render( const uint M, const QSize &size ){ /// render with margin/spacing @param M and @param size QPoint point; QRect rect( point, size ); // From qt sources const uint xround = (M * 200) / size.width(); const uint yround = (M * 200) / size.height(); { /// apply the mask static QBitmap mask; mask.resize( size ); mask.fill( Qt::black ); QPainter p( &mask ); p.setBrush( Qt::white ); p.drawRoundRect( rect, xround, yround ); setMask( mask ); } QColor shadowColor; { int h,s,v; foregroundColor().getHsv( &h, &s, &v ); shadowColor = v > 128 ? Qt::black : Qt::white; } int align = Qt::AlignCenter | WordBreak; m_buffer.resize( rect.size() ); QPainter p( &m_buffer ); if( m_translucency ) { KPixmap background( m_screenshot ); KPixmapEffect::fade( background, 0.80, backgroundColor() ); p.drawPixmap( 0, 0, background ); } else p.fillRect( rect, backgroundColor() ); p.setPen( backgroundColor().dark() ); p.drawRoundRect( rect, xround, yround ); rect.addCoords( M, M, -M, -M ); if( !m_cover.isNull() ) { QRect r( rect ); r.setTop( (size.height() - m_scaledCover.height()) / 2 ); r.setSize( m_scaledCover.size() ); if( !m_scaledCover.hasAlpha() && m_drawShadow && ( m_scaledCover.width() > 22 || m_scaledCover.height() > 22 ) ) { // don't draw a shadow for eg, the Amarok icon QImage shadow; const uint shadowSize = static_cast<uint>( m_scaledCover.width() / 100.0 * 6.0 ); const QString folder = Amarok::saveLocation( "covershadow-cache/" ); const QString file = QString( "shadow_albumcover%1x%2.png" ).arg( m_scaledCover.width() + shadowSize ) .arg( m_scaledCover.height() + shadowSize ); if ( QFile::exists( folder + file ) ) shadow.load( folder + file ); else { shadow.load( locate( "data", "amarok/images/shadow_albumcover.png" ) ); shadow = shadow.smoothScale( m_scaledCover.width() + shadowSize, m_scaledCover.height() + shadowSize ); shadow.save( folder + file, "PNG" ); } QPixmap target; target.convertFromImage( shadow ); //FIXME slow copyBlt( &target, 0, 0, &m_scaledCover ); m_scaledCover = target; r.setTop( (size.height() - m_scaledCover.height()) / 2 ); r.setSize( m_scaledCover.size() ); } p.drawPixmap( r.topLeft(), m_scaledCover ); rect.rLeft() += m_scaledCover.width() + M; } if( m_volume ) { QPixmap vol; vol = QPixmap( rect.width(), rect.height() + fontMetrics().height() / 4 ); QPixmap buf( vol.size() ); QRect r( rect ); r.setLeft( rect.left() + rect.width() / 2 - vol.width() / 2 ); r.setTop( size.height() / 2 - vol.height() / 2); KPixmap pixmapGradient; { // gradient QBitmap mask; mask.resize( vol.size() ); mask.fill( Qt::black ); QPainter p( &mask ); p.setBrush( Qt::white ); p.drawRoundRect ( 3, 3, vol.width() - 6, vol.height() - 6, M * 300 / vol.width(), 99 ); p.end(); pixmapGradient = QPixmap( vol.size() ); KPixmapEffect::gradient( pixmapGradient, colorGroup().background(), colorGroup().highlight(), KPixmapEffect::EllipticGradient ); pixmapGradient.setMask( mask ); } if( m_translucency ) { KPixmap background( m_screenshot ); KPixmapEffect::fade( background, 0.80, backgroundColor() ); bitBlt( &vol, -r.left(), -r.top(), &background ); } else vol.fill( backgroundColor() ); { // vol ( bg-alpha ) static QBitmap mask; mask.resize( vol.size() ); mask.fill( Qt::white ); QPainter p( &mask ); p.setBrush( Qt::black ); p.drawRoundRect ( 1, 1, rect.width()-2, rect.height() + fontMetrics().height() / 4 - 2, M * 300 / vol.width(), 99 ); p.setBrush( Qt::white ); p.drawRoundRect ( 3, 3, vol.width() - 6, vol.height() - 6, M * 300 / vol.width(), 99 ); p.end(); vol.setMask( mask ); } buf.fill( backgroundColor().dark() ); const int offset = int( double( vol.width() * m_newvolume ) / 100 ); bitBlt( &buf, 0, 0, &vol ); // bg bitBlt( &buf, 0, 0, &pixmapGradient, 0, 0, offset ); p.drawPixmap( r.left(), r.top(), buf ); m_volume = false; } QPixmap* star = StarManager::instance()->getStar( m_rating/2, true ); int graphicsHeight = 0; if( useMoodbar() ) { QPixmap moodbar = m_moodbarBundle.moodbar().draw( rect.width(), MOODBAR_HEIGHT ); QRect r( rect ); r.setTop( rect.bottom() - moodbar.height() - (m_rating ? star->height() + M : 0) ); graphicsHeight += moodbar.height() + M; p.drawPixmap( r.left(), r.top(), moodbar ); m_moodbarBundle = MetaBundle(); } if( m_rating > 0 ) { QRect r( rect ); //Align to center... r.setLeft(( rect.left() + rect.width() / 2 ) - star->width() * m_rating / 4 ); r.setTop( rect.bottom() - star->height() ); graphicsHeight += star->height() + M; bool half = m_rating%2; if( half ) { QPixmap* halfStar = StarManager::instance()->getHalfStar( m_rating/2 + 1, true ); p.drawPixmap( r.left() + star->width() * ( m_rating / 2 ), r.top(), *halfStar ); star = StarManager::instance()->getStar( m_rating/2 + 1, true ); } for( int i = 0; i < m_rating/2; i++ ) { p.drawPixmap( r.left() + i * star->width(), r.top(), *star ); } m_rating = 0; } rect.setBottom( rect.bottom() - graphicsHeight ); if( m_drawShadow ) { QPixmap pixmap( rect.size() + QSize(10,10) ); pixmap.fill( Qt::black ); pixmap.setMask( pixmap.createHeuristicMask( true ) ); QPainter p2( &pixmap ); p2.setFont( font() );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -