|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
tracktooltip.cpp
|
|
|
|
begin: Tue 10 Feb 2004
|
|
|
|
copyright: (C) 2004 by Christian Muehlhaeuser
|
|
|
|
email: chris@chris.de
|
|
|
|
|
|
|
|
copyright: (C) 2005 by Gábor Lehel
|
|
|
|
email: illissius@gmail.com
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "amarok.h"
|
|
|
|
#include "app.h"
|
|
|
|
#include "amarokconfig.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "metabundle.h"
|
|
|
|
#include "moodbar.h"
|
|
|
|
#include "collectiondb.h"
|
|
|
|
#include "playlist.h"
|
|
|
|
#include "playlistitem.h"
|
|
|
|
#include "podcastbundle.h"
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "tracktooltip.h"
|
|
|
|
|
|
|
|
|
|
|
|
TrackToolTip *TrackToolTip::instance()
|
|
|
|
{
|
|
|
|
static TrackToolTip tip;
|
|
|
|
return &tip;
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackToolTip::TrackToolTip(): m_haspos( false )
|
|
|
|
{
|
|
|
|
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 & ) ) );
|
|
|
|
connect( Playlist::instance(), TQT_SIGNAL( columnsChanged() ), this, TQT_SLOT( slotUpdate() ) );
|
|
|
|
connect( CollectionDB::instance(), TQT_SIGNAL( scoreChanged( const TQString&, float ) ),
|
|
|
|
this, TQT_SLOT( slotUpdate( const TQString& ) ) );
|
|
|
|
connect( CollectionDB::instance(), TQT_SIGNAL( ratingChanged( const TQString&, int ) ),
|
|
|
|
this, TQT_SLOT( slotUpdate( const TQString& ) ) );
|
|
|
|
// Only connect this once -- m_tags exists for the lifetime of this instance
|
|
|
|
connect( &m_tags.moodbar(), TQT_SIGNAL( jobEvent( int ) ),
|
|
|
|
TQT_SLOT( slotMoodbarEvent() ) );
|
|
|
|
// This is so the moodbar can be re-rendered when AlterMood is changed
|
|
|
|
connect( App::instance(), TQT_SIGNAL( moodbarPrefs( bool, bool, int, bool ) ),
|
|
|
|
TQT_SLOT( slotMoodbarEvent() ) );
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::addToWidget( TQWidget *widget )
|
|
|
|
{
|
|
|
|
if( widget && !m_widgets.containsRef( widget ) )
|
|
|
|
{
|
|
|
|
m_widgets.append( widget );
|
|
|
|
Amarok::ToolTip::add( this, widget );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::removeFromWidget( TQWidget *widget )
|
|
|
|
{
|
|
|
|
if( widget && m_widgets.containsRef( widget ) )
|
|
|
|
{
|
|
|
|
Amarok::ToolTip::remove( widget );
|
|
|
|
m_widgets.removeRef( widget );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define MOODBAR_WIDTH 150
|
|
|
|
|
|
|
|
void TrackToolTip::setTrack( const MetaBundle &tags, bool force )
|
|
|
|
{
|
|
|
|
if( force || m_tags != tags || m_tags.url() != tags.url() )
|
|
|
|
{
|
|
|
|
m_haspos = false;
|
|
|
|
m_tooltip = TQString();
|
|
|
|
|
|
|
|
TQStringList left, right;
|
|
|
|
const TQString tableRow = "<tr><td width=70 align=right>%1:</td><td align=left>%2</td></tr>";
|
|
|
|
|
|
|
|
TQString filename = "", title = ""; //special case these, put the first one encountered on top
|
|
|
|
|
|
|
|
Playlist *playlist = Playlist::instance();
|
|
|
|
const int n = playlist->numVisibleColumns();
|
|
|
|
for( int i = 0; i < n; ++i )
|
|
|
|
{
|
|
|
|
const int column = playlist->mapToLogicalColumn( i );
|
|
|
|
|
|
|
|
if( column == PlaylistItem::Score )
|
|
|
|
{
|
|
|
|
const float score = CollectionDB::instance()->getSongPercentage( tags.url().path() );
|
|
|
|
if( score > 0.f )
|
|
|
|
{
|
|
|
|
right << TQString::number( score, 'f', 2 ); // 2 digits after decimal point
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( column == PlaylistItem::Rating )
|
|
|
|
{
|
|
|
|
const int rating = CollectionDB::instance()->getSongRating( tags.url().path() );
|
|
|
|
if( rating > 0 )
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
for( int i = 0; i < rating / 2; ++i )
|
|
|
|
s += TQString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
|
|
|
|
.arg( locate( "data", "amarok/images/star.png" ) )
|
|
|
|
.arg( TQFontMetrics( TQToolTip::font() ).height() )
|
|
|
|
.arg( TQFontMetrics( TQToolTip::font() ).height() );
|
|
|
|
if( rating % 2 )
|
|
|
|
s += TQString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
|
|
|
|
.arg( locate( "data", "amarok/images/smallstar.png" ) )
|
|
|
|
.arg( TQFontMetrics( TQToolTip::font() ).height() )
|
|
|
|
.arg( TQFontMetrics( TQToolTip::font() ).height() );
|
|
|
|
right << s;
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( column == PlaylistItem::Mood )
|
|
|
|
{
|
|
|
|
if( !AmarokConfig::showMoodbar() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m_tags.moodbar().load();
|
|
|
|
|
|
|
|
switch( tags.moodbar_const().state() )
|
|
|
|
{
|
|
|
|
case Moodbar::JobQueued:
|
|
|
|
case Moodbar::JobRunning:
|
|
|
|
right << tags.prettyText( column );
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Moodbar::Loaded:
|
|
|
|
{
|
|
|
|
// Ok so this is a hack, but it works quite well.
|
|
|
|
// Save an image in the user's home directory just so
|
|
|
|
// it can be referenced in an <img> tag. Store which
|
|
|
|
// moodbar is saved in m_moodbarURL so we don't have
|
|
|
|
// to re-save it every second.
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
TQString filename = ::locateLocal( "data",
|
|
|
|
"amarok/mood_tooltip.png" );
|
|
|
|
int height = TQFontMetrics( TQToolTip::font() ).height() - 2;
|
|
|
|
|
|
|
|
if( m_moodbarURL != tags.url().url() )
|
|
|
|
{
|
|
|
|
TQPixmap moodbar
|
|
|
|
= const_cast<MetaBundle&>( tags ).moodbar().draw(
|
|
|
|
MOODBAR_WIDTH, height );
|
|
|
|
moodbar.save( filename, "PNG", 100 );
|
|
|
|
m_moodbarURL = tags.url().url();
|
|
|
|
}
|
|
|
|
|
|
|
|
right << TQString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
|
|
|
|
.arg( filename ).arg( height ).arg( MOODBAR_WIDTH );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// no tag
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( column == PlaylistItem::PlayCount )
|
|
|
|
{
|
|
|
|
const int count = CollectionDB::instance()->getPlayCount( tags.url().path() );
|
|
|
|
if( count > 0 )
|
|
|
|
{
|
|
|
|
right << TQString::number( count );
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( column == PlaylistItem::LastPlayed )
|
|
|
|
{
|
|
|
|
const uint lastPlayed = CollectionDB::instance()->getLastPlay( tags.url().path() ).toTime_t();
|
|
|
|
right << Amarok::verboseTimeSince( lastPlayed );
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
}
|
|
|
|
else if( column == PlaylistItem::Filename && title.isEmpty() )
|
|
|
|
filename = tags.prettyText( column );
|
|
|
|
else if( column == PlaylistItem::Title && filename.isEmpty() )
|
|
|
|
title = tags.prettyText( column );
|
|
|
|
else if( column != PlaylistItem::Length )
|
|
|
|
{
|
|
|
|
const TQString tag = tags.prettyText( column );
|
|
|
|
if( !tag.isEmpty() )
|
|
|
|
{
|
|
|
|
right << tag;
|
|
|
|
left << playlist->columnText( column );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !filename.isEmpty() )
|
|
|
|
{
|
|
|
|
right.prepend( filename );
|
|
|
|
left.prepend( playlist->columnText( PlaylistItem::Filename ) );
|
|
|
|
}
|
|
|
|
else if( !title.isEmpty() )
|
|
|
|
{
|
|
|
|
right.prepend( title );
|
|
|
|
left.prepend( playlist->columnText( PlaylistItem::Title ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( tags.length() > 0 ) //special case this too, always on the bottom
|
|
|
|
{
|
|
|
|
m_haspos = true;
|
|
|
|
right << "%9 / " + tags.prettyLength();
|
|
|
|
left << playlist->columnText( PlaylistItem::Length );
|
|
|
|
}
|
|
|
|
|
|
|
|
//NOTE it seems to be necessary to <center> each element indivdually
|
|
|
|
m_tooltip += "<center><b>Amarok</b></center><table cellpadding='2' cellspacing='2' align='center'><tr>";
|
|
|
|
|
|
|
|
m_tooltip += "%1"; //the cover gets substituted in, in tooltip()
|
|
|
|
m_cover = CollectionDB::instance()->podcastImage( tags, true );
|
|
|
|
if( m_cover.isEmpty() || m_cover.contains( "nocover" ) != -1 )
|
|
|
|
{
|
|
|
|
m_cover = CollectionDB::instance()->albumImage( tags, true, 150 );
|
|
|
|
if ( m_cover == CollectionDB::instance()->notAvailCover() )
|
|
|
|
m_cover = TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_tooltip += "<td><table cellpadding='0' cellspacing='0'>";
|
|
|
|
|
|
|
|
if (tags.title().isEmpty() || tags.artist().isEmpty())
|
|
|
|
// no title or no artist, so we add prettyTitle
|
|
|
|
m_tooltip += TQString ("<tr><td align=center colspan='2'>%1</td></tr>")
|
|
|
|
.arg(tags.veryNiceTitle());
|
|
|
|
for( uint x = 0; x < left.count(); ++x )
|
|
|
|
if ( !right[x].isEmpty() )
|
|
|
|
m_tooltip += tableRow.arg( left[x] ).arg( right[x] );
|
|
|
|
|
|
|
|
m_tooltip += "</table></td>";
|
|
|
|
m_tooltip += "</tr></table></center>";
|
|
|
|
|
|
|
|
m_tags = tags;
|
|
|
|
updateWidgets();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::setPos( int pos )
|
|
|
|
{
|
|
|
|
if( m_pos != pos )
|
|
|
|
{
|
|
|
|
m_pos = pos;
|
|
|
|
updateWidgets();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::clear()
|
|
|
|
{
|
|
|
|
m_pos = 0;
|
|
|
|
m_cover = TQString();
|
|
|
|
m_tooltip = i18n( "Amarok - rediscover your music" );
|
|
|
|
m_tags = MetaBundle();
|
|
|
|
m_tags.setUrl( KURL() );
|
|
|
|
|
|
|
|
updateWidgets();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPair<TQString, TQRect> TrackToolTip::toolTipText( TQWidget*, const TQPoint& ) const
|
|
|
|
{
|
|
|
|
return TQPair<TQString, TQRect>( tooltip(), TQRect() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::slotCoverChanged( const TQString &artist, const TQString &album )
|
|
|
|
{
|
|
|
|
if( artist == m_tags.artist() && album == m_tags.album() )
|
|
|
|
{
|
|
|
|
m_cover = CollectionDB::instance()->albumImage( m_tags, true, 150 );
|
|
|
|
if( m_cover == CollectionDB::instance()->notAvailCover() )
|
|
|
|
m_cover = TQString();
|
|
|
|
|
|
|
|
updateWidgets();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::slotImageChanged( const TQString &remoteURL )
|
|
|
|
{
|
|
|
|
PodcastEpisodeBundle peb;
|
|
|
|
if( CollectionDB::instance()->getPodcastEpisodeBundle( m_tags.url().url(), &peb ) )
|
|
|
|
{
|
|
|
|
PodcastChannelBundle pcb;
|
|
|
|
if( CollectionDB::instance()->getPodcastChannelBundle( peb.parent().url(), &pcb ) )
|
|
|
|
{
|
|
|
|
if( pcb.imageURL().url() == remoteURL )
|
|
|
|
{
|
|
|
|
m_cover = CollectionDB::instance()->podcastImage( remoteURL );
|
|
|
|
if( m_cover == CollectionDB::instance()->notAvailCover() )
|
|
|
|
m_cover = TQString();
|
|
|
|
|
|
|
|
updateWidgets();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::slotUpdate( const TQString &url )
|
|
|
|
{
|
|
|
|
if( url.isNull() || url == m_tags.url().path() )
|
|
|
|
setTrack( m_tags, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TrackToolTip::slotMoodbarEvent( void )
|
|
|
|
{
|
|
|
|
// Clear this so the moodbar gets redrawn
|
|
|
|
m_moodbarURL = TQString();
|
|
|
|
// Reset the moodbar in case AlterMood has changed
|
|
|
|
m_tags.moodbar().reset();
|
|
|
|
|
|
|
|
setTrack( m_tags, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString TrackToolTip::tooltip() const
|
|
|
|
{
|
|
|
|
TQString tip = m_tooltip;;
|
|
|
|
if( !m_tags.isEmpty() )
|
|
|
|
{
|
|
|
|
if( !m_cover.isEmpty() )
|
|
|
|
tip = tip.arg( TQString( "<td><table cellpadding='0' cellspacing='0'><tr><td>"
|
|
|
|
"<img src='%1'>"
|
|
|
|
"</td></tr></table></td>" ).arg( m_cover ) );
|
|
|
|
else
|
|
|
|
tip = tip.arg("");
|
|
|
|
if( m_haspos )
|
|
|
|
tip = tip.arg( MetaBundle::prettyLength( m_pos / 1000, true ) );
|
|
|
|
}
|
|
|
|
return tip;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackToolTip::updateWidgets()
|
|
|
|
{
|
|
|
|
Amarok::ToolTip::updateTip();
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "tracktooltip.moc"
|