|
|
|
/*
|
|
|
|
* 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 <tqbitmap.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqvaluevector.h>
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
|
|
|
|
namespace ShadowEngine
|
|
|
|
{
|
|
|
|
TQImage makeShadow( const TQPixmap &textPixmap, const TQColor &bgColor );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define MOODBAR_HEIGHT 20
|
|
|
|
|
|
|
|
|
|
|
|
OSDWidget::OSDWidget( TQWidget *parent, const char *name )
|
|
|
|
: TQWidget( parent, name, WType_TopLevel | WNoAutoErase | WStyle_Customize | WX11BypassWM | WStyle_StaysOnTop )
|
|
|
|
, m_duration( 2000 )
|
|
|
|
, m_timer( new TQTimer( this ) )
|
|
|
|
, m_alignment( Middle )
|
|
|
|
, m_screen( 0 )
|
|
|
|
, m_y( MARGIN )
|
|
|
|
, m_drawShadow( false )
|
|
|
|
, m_translucency( false )
|
|
|
|
, m_rating( 0 )
|
|
|
|
, m_volume( false )
|
|
|
|
{
|
|
|
|
setFocusPolicy( TQ_NoFocus );
|
|
|
|
setBackgroundMode( NoBackground );
|
|
|
|
unsetColors();
|
|
|
|
|
|
|
|
connect( m_timer, TQT_SIGNAL(timeout()), TQT_SLOT(hide()) );
|
|
|
|
connect( CollectionDB::instance(), TQT_SIGNAL( ratingChanged( const TQString&, int ) ),
|
|
|
|
this, TQT_SLOT( ratingChanged( const TQString&, int ) ) );
|
|
|
|
|
|
|
|
//or crashes, KWin bug I think, crashes in TQWidget::icon()
|
|
|
|
kapp->setTopWidget( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::show( const TQString &text, TQImage newImage )
|
|
|
|
{
|
|
|
|
#ifdef TQ_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
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::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();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::ratingChanged( const TQString& path, int rating )
|
|
|
|
{
|
|
|
|
const MetaBundle ¤tTrack = EngineController::instance()->bundle();
|
|
|
|
if( currentTrack.isFile() && currentTrack.url().path() == path )
|
|
|
|
ratingChanged( rating );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::show() //virtual
|
|
|
|
{
|
|
|
|
#ifdef TQ_WS_X11
|
|
|
|
if ( !isEnabled() || m_text.isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
const uint M = fontMetrics().width( 'x' );
|
|
|
|
|
|
|
|
const TQRect oldGeometry = TQRect( pos(), size() );
|
|
|
|
const TQRect newGeometry = determineMetrics( M );
|
|
|
|
|
|
|
|
if( m_translucency && !isShown() || !newGeometry.intersects( oldGeometry ) )
|
|
|
|
m_screenshot = TQPixmap(TQPixmap::grabWindow( qt_xrootwin(),
|
|
|
|
newGeometry.x(), newGeometry.y(),
|
|
|
|
newGeometry.width(), newGeometry.height() ));
|
|
|
|
|
|
|
|
|
|
|
|
else if ( m_translucency )
|
|
|
|
{
|
|
|
|
const TQRect unite = oldGeometry.unite( newGeometry );
|
|
|
|
KPixmap pix = TQPixmap(TQPixmap::grabWindow( qt_xrootwin(), unite.x(), unite.y(), unite.width(), unite.height() ));
|
|
|
|
|
|
|
|
TQPoint p = oldGeometry.topLeft() - unite.topLeft();
|
|
|
|
bitBlt( &pix, p, &m_screenshot );
|
|
|
|
|
|
|
|
m_screenshot.resize( newGeometry.size() );
|
|
|
|
|
|
|
|
p = newGeometry.topLeft() - unite.topLeft();
|
|
|
|
if (!kapp->isX11CompositionAvailable())
|
|
|
|
bitBlt( &m_screenshot, 0, 0, &pix, p.x(), p.y() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( newGeometry.width() > 0 && newGeometry.height() > 0 )
|
|
|
|
{
|
|
|
|
render( M, newGeometry.size() );
|
|
|
|
setGeometry( newGeometry );
|
|
|
|
TQWidget::show();
|
|
|
|
paintMe();
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
TQRect
|
|
|
|
OSDWidget::determineMetrics( const uint M )
|
|
|
|
{
|
|
|
|
// sometimes we only have a tiddly cover
|
|
|
|
const TQSize minImageSize = m_cover.size().boundedTo( TQSize(100,100) );
|
|
|
|
|
|
|
|
// determine a sensible maximum size, don't cover the whole desktop or cross the screen
|
|
|
|
const TQSize margin( (M + MARGIN) * 2, (M + MARGIN) * 2 ); //margins
|
|
|
|
const TQSize image = m_cover.isNull() ? TQSize( 0, 0 ) : minImageSize;
|
|
|
|
const TQSize max = TQApplication::desktop()->screen( m_screen )->size() - margin;
|
|
|
|
|
|
|
|
// If we don't do that, the boundingRect() might not be suitable for drawText() (TQt issue N67674)
|
|
|
|
m_text.replace( TQRegExp(" +\n"), "\n" );
|
|
|
|
// remove consecutive line breaks
|
|
|
|
m_text.replace( TQRegExp("\n+"), "\n" );
|
|
|
|
|
|
|
|
// The osd cannot be larger than the screen
|
|
|
|
TQRect rect = fontMetrics().boundingRect( 0, 0,
|
|
|
|
max.width() - image.width(), max.height(),
|
|
|
|
AlignCenter | WordBreak, m_text );
|
|
|
|
|
|
|
|
if( m_volume )
|
|
|
|
{
|
|
|
|
static const TQString tmp = TQString ("******").insert( 3,
|
|
|
|
( i18n("Volume: 100%").length() >= i18n("Mute").length() )?
|
|
|
|
i18n("Volume: 100%") : i18n("Mute") );
|
|
|
|
|
|
|
|
TQRect 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 )
|
|
|
|
{
|
|
|
|
TQPixmap* 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(
|
|
|
|
TQMIN( availableWidth, m_cover.width() ),
|
|
|
|
TQMIN( rect.height(), m_cover.height() ),
|
|
|
|
TQ_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 TQSize newSize = rect.size();
|
|
|
|
const TQRect screen = TQApplication::desktop()->screenGeometry( m_screen );
|
|
|
|
TQPoint 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 TQRect( newPos, rect.size() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::render( const uint M, const TQSize &size )
|
|
|
|
{
|
|
|
|
/// render with margin/spacing @param M and @param size
|
|
|
|
|
|
|
|
TQPoint point;
|
|
|
|
TQRect rect( point, size );
|
|
|
|
|
|
|
|
// From qt sources
|
|
|
|
const uint xround = (M * 200) / size.width();
|
|
|
|
const uint yround = (M * 200) / size.height();
|
|
|
|
|
|
|
|
{ /// apply the mask
|
|
|
|
static TQBitmap mask;
|
|
|
|
|
|
|
|
mask.resize( size );
|
|
|
|
mask.fill( TQt::black );
|
|
|
|
|
|
|
|
TQPainter p( &mask );
|
|
|
|
p.setBrush( TQt::white );
|
|
|
|
p.drawRoundRect( rect, xround, yround );
|
|
|
|
setMask( mask );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQColor shadowColor;
|
|
|
|
{
|
|
|
|
int h,s,v;
|
|
|
|
foregroundColor().getHsv( &h, &s, &v );
|
|
|
|
shadowColor = v > 128 ? TQt::black : TQt::white;
|
|
|
|
}
|
|
|
|
|
|
|
|
int align = TQt::AlignCenter | WordBreak;
|
|
|
|
|
|
|
|
m_buffer.resize( rect.size() );
|
|
|
|
TQPainter p( &m_buffer );
|
|
|
|
|
|
|
|
if (( m_translucency ) && (!kapp->isX11CompositionAvailable()))
|
|
|
|
{
|
|
|
|
KPixmap background( m_screenshot );
|
|
|
|
KPixmapEffect::fade( background, 0.80, backgroundColor() );
|
|
|
|
p.drawPixmap( 0, 0, background );
|
|
|
|
}
|
|
|
|
else if (( m_translucency ) && (kapp->isX11CompositionAvailable()))
|
|
|
|
{
|
|
|
|
// Make the background semi-transparent
|
|
|
|
TQPixmap background( m_screenshot.width(), m_screenshot.height(), 32 );
|
|
|
|
TQRgb blend_color = tqRgba(backgroundColor().red(), backgroundColor().green(), backgroundColor().blue(), 204); // RGBA
|
|
|
|
float alpha = tqAlpha(blend_color) / 255.0;
|
|
|
|
int pixel = tqAlpha(blend_color) << 24 | int(tqRed(blend_color) * alpha) << 16 | int(tqGreen(blend_color) * alpha) << 8 | int(tqBlue(blend_color) * alpha);
|
|
|
|
background.fill(TQColor(blend_color, pixel));
|
|
|
|
bitBlt( &m_buffer, 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() )
|
|
|
|
{
|
|
|
|
TQRect 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
|
|
|
|
TQImage shadow;
|
|
|
|
const uint shadowSize = static_cast<uint>( m_scaledCover.width() / 100.0 * 6.0 );
|
|
|
|
|
|
|
|
const TQString folder = Amarok::saveLocation( "covershadow-cache/" );
|
|
|
|
const TQString file = TQString( "shadow_albumcover%1x%2.png" ).arg( m_scaledCover.width() + shadowSize )
|
|
|
|
.arg( m_scaledCover.height() + shadowSize );
|
|
|
|
if ( TQFile::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" );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPixmap 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 )
|
|
|
|
{
|
|
|
|
TQPixmap vol;
|
|
|
|
vol = TQPixmap( rect.width(), rect.height() + fontMetrics().height() / 4 );
|
|
|
|
|
|
|
|
TQPixmap buf( vol.size() );
|
|
|
|
TQRect r( rect );
|
|
|
|
r.setLeft( rect.left() + rect.width() / 2 - vol.width() / 2 );
|
|
|
|
r.setTop( size.height() / 2 - vol.height() / 2);
|
|
|
|
|
|
|
|
KPixmap pixmapGradient;
|
|
|
|
{ // gradient
|
|
|
|
TQBitmap mask;
|
|
|
|
mask.resize( vol.size() );
|
|
|
|
mask.fill( TQt::black );
|
|
|
|
|
|
|
|
TQPainter p( &mask );
|
|
|
|
p.setBrush( TQt::white );
|
|
|
|
p.drawRoundRect ( 3, 3, vol.width() - 6, vol.height() - 6,
|
|
|
|
M * 300 / vol.width(), 99 );
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
pixmapGradient = TQPixmap( vol.size() );
|
|
|
|
KPixmapEffect::gradient( pixmapGradient, colorGroup().background(),
|
|
|
|
colorGroup().highlight(), KPixmapEffect::EllipticGradient );
|
|
|
|
pixmapGradient.setMask( mask );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (( m_translucency ) && (!kapp->isX11CompositionAvailable()))
|
|
|
|
{
|
|
|
|
KPixmap background( m_screenshot );
|
|
|
|
KPixmapEffect::fade( background, 0.80, backgroundColor() );
|
|
|
|
bitBlt( &vol, -r.left(), -r.top(), &background );
|
|
|
|
}
|
|
|
|
else if (( m_translucency ) && (kapp->isX11CompositionAvailable()))
|
|
|
|
{
|
|
|
|
// Make the background semi-transparent
|
|
|
|
TQPixmap background( m_screenshot.width(), m_screenshot.height(), 32 );
|
|
|
|
TQRgb blend_color = tqRgba(backgroundColor().red(), backgroundColor().green(), backgroundColor().blue(), 204); // RGBA
|
|
|
|
float alpha = tqAlpha(blend_color) / 255.0;
|
|
|
|
int pixel = tqAlpha(blend_color) << 24 | int(tqRed(blend_color) * alpha) << 16 | int(tqGreen(blend_color) * alpha) << 8 | int(tqBlue(blend_color) * alpha);
|
|
|
|
background.fill(TQColor(blend_color, pixel));
|
|
|
|
bitBlt( &vol, -r.left(), -r.top(), &background );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
vol.fill( backgroundColor() );
|
|
|
|
|
|
|
|
{ // vol ( bg-alpha )
|
|
|
|
static TQBitmap mask;
|
|
|
|
mask.resize( vol.size() );
|
|
|
|
mask.fill( TQt::white );
|
|
|
|
|
|
|
|
TQPainter p( &mask );
|
|
|
|
p.setBrush( TQt::black );
|
|
|
|
p.drawRoundRect ( 1, 1, rect.width()-2, rect.height() + fontMetrics().height() / 4 - 2,
|
|
|
|
M * 300 / vol.width(), 99 );
|
|
|
|
p.setBrush( TQt::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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPixmap* star = StarManager::instance()->getStar( m_rating/2, true );
|
|
|
|
int graphicsHeight = 0;
|
|
|
|
|
|
|
|
if( useMoodbar() )
|
|
|
|
{
|
|
|
|
TQPixmap moodbar
|
|
|
|
= m_moodbarBundle.moodbar().draw( rect.width(), MOODBAR_HEIGHT );
|
|
|
|
TQRect 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 )
|
|
|
|
{
|
|
|
|
TQRect 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 )
|
|
|
|
{
|
|
|
|
TQPixmap* 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 )
|
|
|
|
{
|
|
|
|
TQPixmap pixmap( rect.size() + TQSize(10,10) );
|
|
|
|
pixmap.fill( TQt::black );
|
|
|
|
pixmap.setMask( pixmap.createHeuristicMask( true ) );
|
|
|
|
|
|
|
|
TQPainter p2( &pixmap );
|
|
|
|
p2.setFont( font() );
|
|
|
|
p2.setPen( TQt::white );
|
|
|
|
p2.setBrush( TQt::white );
|
|
|
|
p2.drawText( TQRect(TQPoint(5,5), rect.size()), align , m_text );
|
|
|
|
p2.end();
|
|
|
|
|
|
|
|
p.drawImage( rect.topLeft() - TQPoint(5,5), ShadowEngine::makeShadow( pixmap, shadowColor ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
p.setPen( foregroundColor() );
|
|
|
|
p.setFont( font() );
|
|
|
|
p.drawText( rect, align, m_text );
|
|
|
|
p.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::paintMe()
|
|
|
|
{
|
|
|
|
if ((m_translucency) && (kapp->isX11CompositionAvailable())) {
|
|
|
|
// We have true composition support, so make the OSD truly transparent
|
|
|
|
TQImage blendedImage = m_buffer.convertToImage();
|
|
|
|
blendedImage = blendedImage.convertDepth(32);
|
|
|
|
blendedImage.setAlphaBuffer(true);
|
|
|
|
|
|
|
|
// Convert the ARGB pixmap to an ARGB image
|
|
|
|
// NOTE 1: TQPixmap::convertToImage() always converts an ARGB pixmap into an RGB image
|
|
|
|
// NOTE 2: This should eventually make its way into kdelibs or Qt itself,
|
|
|
|
// as it would also be useful in applications other than Amarok
|
|
|
|
int w = blendedImage.width();
|
|
|
|
int h = blendedImage.height();
|
|
|
|
Pixmap rawpixmap = m_buffer.handle();
|
|
|
|
XImage *image;
|
|
|
|
image = XGetImage (qt_xdisplay(), rawpixmap, 0, 0, w, h, AllPlanes, XYPixmap);
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
|
|
TQRgb *ls = (TQRgb *)blendedImage.scanLine( y );
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
|
|
unsigned int rawpixel = XGetPixel(image, x, y);
|
|
|
|
int r = int( (rawpixel & 0x00ff0000) >> 16 );
|
|
|
|
int g = int( (rawpixel & 0x0000ff00) >> 8 );
|
|
|
|
int b = int( (rawpixel & 0x000000ff) );
|
|
|
|
int a = int( (rawpixel & 0xff000000) >> 24 );
|
|
|
|
ls[x] = tqRgba( r, g, b, a );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree (image);
|
|
|
|
|
|
|
|
// Finally, paint it
|
|
|
|
TQPainter p1;
|
|
|
|
p1.begin( this );
|
|
|
|
blendedImage.setAlphaBuffer(false);
|
|
|
|
p1.drawImage( 0, 0, blendedImage );
|
|
|
|
p1.end();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bitBlt( this, 0, 0, &m_buffer );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OSDWidget::event( TQEvent *e )
|
|
|
|
{
|
|
|
|
switch( e->type() )
|
|
|
|
{
|
|
|
|
case TQEvent::ApplicationPaletteChange:
|
|
|
|
if( !AmarokConfig::osdUseCustomColors() )
|
|
|
|
unsetColors(); //use new palette's colours
|
|
|
|
return true;
|
|
|
|
case TQEvent::Paint:
|
|
|
|
paintMe();
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return TQWidget::event( e );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::mousePressEvent( TQMouseEvent* )
|
|
|
|
{
|
|
|
|
hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::unsetColors()
|
|
|
|
{
|
|
|
|
const TQColorGroup c = TQApplication::palette().active();
|
|
|
|
|
|
|
|
setPaletteForegroundColor( c.highlightedText() );
|
|
|
|
setPaletteBackgroundColor( c.highlight() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OSDWidget::setScreen( int screen )
|
|
|
|
{
|
|
|
|
const int n = TQApplication::desktop()->numScreens();
|
|
|
|
m_screen = (screen >= n) ? n-1 : screen;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
OSDWidget::useMoodbar( void )
|
|
|
|
{
|
|
|
|
return (m_moodbarBundle.moodbar().state() == Moodbar::Loaded &&
|
|
|
|
AmarokConfig::showMoodbar() );
|
|
|
|
}
|
|
|
|
|
|
|
|
////// OSDPreviewWidget below /////////////////////
|
|
|
|
|
|
|
|
#include <kcursor.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
|
|
|
|
namespace Amarok
|
|
|
|
{
|
|
|
|
TQImage icon() { return TQImage( KIconLoader().iconPath( "amarok", -KIcon::SizeHuge ) ); }
|
|
|
|
}
|
|
|
|
|
|
|
|
OSDPreviewWidget::OSDPreviewWidget( TQWidget *parent )
|
|
|
|
: OSDWidget( parent, "osdpreview" )
|
|
|
|
, m_dragging( false )
|
|
|
|
{
|
|
|
|
m_text = i18n( "OSD Preview - drag to reposition" );
|
|
|
|
m_duration = 0;
|
|
|
|
m_cover = Amarok::icon();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OSDPreviewWidget::mousePressEvent( TQMouseEvent *event )
|
|
|
|
{
|
|
|
|
m_dragOffset = event->pos();
|
|
|
|
|
|
|
|
if( event->button() == Qt::LeftButton && !m_dragging ) {
|
|
|
|
grabMouse( KCursor::sizeAllCursor() );
|
|
|
|
m_dragging = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OSDPreviewWidget::mouseReleaseEvent( TQMouseEvent * /*event*/ )
|
|
|
|
{
|
|
|
|
if( m_dragging )
|
|
|
|
{
|
|
|
|
m_dragging = false;
|
|
|
|
releaseMouse();
|
|
|
|
|
|
|
|
// compute current Position && offset
|
|
|
|
TQDesktopWidget *desktop = TQApplication::desktop();
|
|
|
|
int currentScreen = desktop->screenNumber( pos() );
|
|
|
|
|
|
|
|
if( currentScreen != -1 ) {
|
|
|
|
// set new data
|
|
|
|
m_screen = currentScreen;
|
|
|
|
m_y = TQWidget::y();
|
|
|
|
|
|
|
|
emit positionChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OSDPreviewWidget::mouseMoveEvent( TQMouseEvent *e )
|
|
|
|
{
|
|
|
|
if( m_dragging && this == mouseGrabber() )
|
|
|
|
{
|
|
|
|
// Here we implement a "snap-to-grid" like positioning system for the preview widget
|
|
|
|
|
|
|
|
const TQRect screen = TQApplication::desktop()->screenGeometry( m_screen );
|
|
|
|
const uint hcenter = screen.width() / 2;
|
|
|
|
const uint eGlobalPosX = e->globalPos().x() - screen.left();
|
|
|
|
const uint snapZone = screen.width() / 24;
|
|
|
|
|
|
|
|
TQPoint destination = e->globalPos() - m_dragOffset - screen.topLeft();
|
|
|
|
int maxY = screen.height() - height() - MARGIN;
|
|
|
|
if( destination.y() < MARGIN ) destination.ry() = MARGIN;
|
|
|
|
if( destination.y() > maxY ) destination.ry() = maxY;
|
|
|
|
|
|
|
|
if( eGlobalPosX < (hcenter-snapZone) ) {
|
|
|
|
m_alignment = Left;
|
|
|
|
destination.rx() = MARGIN;
|
|
|
|
}
|
|
|
|
else if( eGlobalPosX > (hcenter+snapZone) ) {
|
|
|
|
m_alignment = Right;
|
|
|
|
destination.rx() = screen.width() - MARGIN - width();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const uint eGlobalPosY = e->globalPos().y() - screen.top();
|
|
|
|
const uint vcenter = screen.height()/2;
|
|
|
|
|
|
|
|
destination.rx() = hcenter - width()/2;
|
|
|
|
|
|
|
|
if( eGlobalPosY >= (vcenter-snapZone) && eGlobalPosY <= (vcenter+snapZone) )
|
|
|
|
{
|
|
|
|
m_alignment = Center;
|
|
|
|
destination.ry() = vcenter - height()/2;
|
|
|
|
}
|
|
|
|
else m_alignment = Middle;
|
|
|
|
}
|
|
|
|
|
|
|
|
destination += screen.topLeft();
|
|
|
|
|
|
|
|
move( destination );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////// Amarok::OSD below /////////////////////
|
|
|
|
|
|
|
|
#include "enginecontroller.h"
|
|
|
|
#include "metabundle.h"
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
Amarok::OSD::OSD(): OSDWidget( 0 )
|
|
|
|
{
|
|
|
|
connect( CollectionDB::instance(), TQT_SIGNAL( coverChanged( const TQString&, const TQString& ) ),
|
|
|
|
this, TQT_SLOT( slotCoverChanged( const TQString&, const TQString& ) ) );
|
|
|
|
connect( CollectionDB::instance(), TQT_SIGNAL( imageFetched( const TQString& ) ),
|
|
|
|
this, TQT_SLOT( slotImageChanged( const TQString& ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Amarok::OSD::show( const MetaBundle &bundle ) //slot
|
|
|
|
{
|
|
|
|
#ifdef TQ_WS_X11
|
|
|
|
TQString text = "";
|
|
|
|
if( bundle.url().isEmpty() )
|
|
|
|
text = i18n( "No track playing" );
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQValueVector<TQString> tags;
|
|
|
|
tags.append(bundle.prettyTitle());
|
|
|
|
for( int i = 0; i < PlaylistItem::NUM_COLUMNS; ++i )
|
|
|
|
tags.append(bundle.prettyText( i ));
|
|
|
|
|
|
|
|
if( bundle.length() <= 0 )
|
|
|
|
tags[PlaylistItem::Length+1] = TQString();
|
|
|
|
|
|
|
|
if( AmarokConfig::osdUsePlaylistColumns() )
|
|
|
|
{
|
|
|
|
TQString tag;
|
|
|
|
TQValueVector<int> availableTags; //eg, ones that aren't empty
|
|
|
|
static const TQValueList<int> parens = //display these in parentheses
|
|
|
|
TQValueList<int>() << PlaylistItem::PlayCount << PlaylistItem::Year << PlaylistItem::Comment
|
|
|
|
<< PlaylistItem::Genre << PlaylistItem::Length << PlaylistItem::Bitrate
|
|
|
|
<< PlaylistItem::LastPlayed << PlaylistItem::Score << PlaylistItem::Filesize;
|
|
|
|
OSDWidget::setMoodbar();
|
|
|
|
OSDWidget::setRating( 0 );
|
|
|
|
for( int i = 0, n = Playlist::instance()->numVisibleColumns(); i < n; ++i )
|
|
|
|
{
|
|
|
|
const int column = Playlist::instance()->mapToLogicalColumn( i );
|
|
|
|
if( !tags.at( column + 1 ).isEmpty() && column != PlaylistItem::Rating )
|
|
|
|
availableTags.append(column);
|
|
|
|
if( column == PlaylistItem::Rating )
|
|
|
|
OSDWidget::setRating( bundle.rating() );
|
|
|
|
else if( column == PlaylistItem::Mood )
|
|
|
|
OSDWidget::setMoodbar( bundle );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( int n = availableTags.count(), i = 0; i < n; ++i )
|
|
|
|
{
|
|
|
|
const int column = availableTags.at( i );
|
|
|
|
TQString append = ( i == 0 ) ? ""
|
|
|
|
: ( n > 1 && i == n / 2 ) ? "\n"
|
|
|
|
: ( parens.contains( column ) || parens.contains( availableTags.at( i - 1 ) ) ) ? " "
|
|
|
|
: i18n(" - ");
|
|
|
|
append += ( parens.contains( column ) ? "(%1)" : "%1" );
|
|
|
|
text += append.arg( tags.at( column + 1 ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQMap<TQString, TQString> args;
|
|
|
|
args["prettytitle"] = bundle.prettyTitle();
|
|
|
|
for( int i = 0; i < PlaylistItem::NUM_COLUMNS; ++i )
|
|
|
|
args[bundle.exactColumnName( i ).lower()] = bundle.prettyText( i );
|
|
|
|
|
|
|
|
if( bundle.length() <= 0 )
|
|
|
|
args["length"] = TQString();
|
|
|
|
|
|
|
|
|
|
|
|
uint time=EngineController::instance()->engine()->position();
|
|
|
|
uint sec=(time/1000)%60; //is there a better way to calculate the time?
|
|
|
|
time /= 1000;
|
|
|
|
uint min=(time/60)%60;
|
|
|
|
time /= 60;
|
|
|
|
uint hour=(time/60)%60;
|
|
|
|
TQString timeformat="";
|
|
|
|
if(hour!=0)
|
|
|
|
{
|
|
|
|
timeformat += TQString::number(hour);
|
|
|
|
timeformat +=":";
|
|
|
|
}
|
|
|
|
timeformat +=TQString::number(min);
|
|
|
|
timeformat +=":";
|
|
|
|
if(sec<10)
|
|
|
|
timeformat +="0";
|
|
|
|
timeformat +=TQString::number(sec);
|
|
|
|
args["elapsed"]=timeformat;
|
|
|
|
QStringx osd = AmarokConfig::osdText();
|
|
|
|
|
|
|
|
// hacky, but works...
|
|
|
|
if( osd.contains( "%rating" ) )
|
|