2 This program is free software; you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation; either version 2 of the License, or
5 (at your option) any later version.
10 begin: Tue 10 Feb 2004
11 copyright: (C) 2004 by Christian Muehlhaeuser
14 copyright: (C) 2005 by Gábor Lehel
15 email: illissius@gmail.com
18 #include "tracktooltip.h"
20 #include "amarokconfig.h"
22 #include "amarok_export.h"
24 #include "collectiondb.h"
26 #include "metabundle.h"
28 #include "podcastbundle.h"
30 #include <KCalendarSystem>
31 #include <KStandardDirs>
33 #include <QApplication>
39 QString AMAROK_EXPORT
verboseTimeSince( const QDateTime
&datetime
)
41 const QDateTime now
= QDateTime::currentDateTime();
42 const int datediff
= datetime
.daysTo( now
);
44 if( datediff
>= 6*7 /*six weeks*/ ) { // return absolute month/year
45 const KCalendarSystem
*cal
= KGlobal::locale()->calendar();
46 const QDate date
= datetime
.date();
47 return i18nc( "monthname year", "%1 %2", cal
->monthName(date
),
48 cal
->yearString(date
, KCalendarSystem::LongFormat
) );
51 //TODO "last week" = maybe within 7 days, but prolly before last sunday
53 if( datediff
>= 7 ) // return difference in weeks
54 return i18np( "One week ago", "%1 weeks ago", (datediff
+3)/7 );
57 return i18n( "Tomorrow" );
59 const int timediff
= datetime
.secsTo( now
);
61 if( timediff
>= 24*60*60 /*24 hours*/ ) // return difference in days
62 return datediff
== 1 ?
64 i18np( "One day ago", "%1 days ago", (timediff
+12*60*60)/(24*60*60) );
66 if( timediff
>= 90*60 /*90 minutes*/ ) // return difference in hours
67 return i18np( "One hour ago", "%1 hours ago", (timediff
+30*60)/(60*60) );
69 //TODO are we too specific here? Be more fuzzy? ie, use units of 5 minutes, or "Recently"
71 if( timediff
>= 0 ) // return difference in minutes
73 i18np( "One minute ago", "%1 minutes ago", (timediff
+30)/60 ) :
74 i18n( "Within the last minute" );
76 return i18n( "The future" );
79 QString
verboseTimeSince( uint
time_t )
82 return i18n( "Never" );
85 dt
.setTime_t( time_t );
86 return verboseTimeSince( dt
);
90 TrackToolTip
*TrackToolTip::instance()
92 static TrackToolTip tip
;
96 TrackToolTip::TrackToolTip(): m_haspos( false )
98 connect( CollectionDB::instance(), SIGNAL( coverChanged( const QString
&, const QString
& ) ),
99 this, SLOT( slotCoverChanged( const QString
&, const QString
& ) ) );
100 connect( CollectionDB::instance(), SIGNAL( imageFetched( const QString
& ) ),
101 this, SLOT( slotImageChanged( const QString
& ) ) );
102 // PORT 2.0 connect( Playlist::instance(), SIGNAL( columnsChanged() ), this, SLOT( slotUpdate() ) );
103 connect( CollectionDB::instance(), SIGNAL( scoreChanged( const QString
&, float ) ),
104 this, SLOT( slotUpdate( const QString
& ) ) );
105 connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString
&, int ) ),
106 this, SLOT( slotUpdate( const QString
& ) ) );
107 // Only connect this once -- m_tags exists for the lifetime of this instance
108 connect( &m_tags
.moodbar(), SIGNAL( jobEvent( int ) ),
109 SLOT( slotMoodbarEvent() ) );
110 // This is so the moodbar can be re-rendered when AlterMood is changed
111 connect( App::instance(), SIGNAL( moodbarPrefs( bool, bool, int, bool ) ),
112 SLOT( slotMoodbarEvent() ) );
116 void TrackToolTip::addToWidget( QWidget
*widget
)
118 if( widget
&& !m_widgets
.containsRef( widget
) )
120 m_widgets
.append( widget
);
121 Amarok::ToolTip::add( this, widget
);
125 void TrackToolTip::removeFromWidget( QWidget
*widget
)
127 if( widget
&& m_widgets
.containsRef( widget
) )
129 Amarok::ToolTip::remove( widget
);
130 m_widgets
.removeRef( widget
);
135 #define MOODBAR_WIDTH 150
137 void TrackToolTip::setTrack( const MetaBundle
&tags
, bool force
)
139 if( force
|| m_tags
!= tags
|| m_tags
.url() != tags
.url() )
144 QStringList left
, right
;
145 const QString tableRow
= "<tr><td width=70 align=right>%1:</td><td align=left>%2</td></tr>";
147 QString filename
= "", title
= ""; //special case these, put the first one encountered on top
149 // Playlist *playlist = Playlist::instance();
150 // const int n = playlist->numVisibleColumns();
151 // for( int i = 0; i < n; ++i )
153 // const int column = playlist->mapToLogicalColumn( i );
155 // if( column == PlaylistItem::Score )
157 // const float score = CollectionDB::instance()->getSongPercentage( tags.url().path() );
160 // right << QString::number( score, 'f', 2 ); // 2 digits after decimal point
161 // left << playlist->columnText( column );
164 // else if( column == PlaylistItem::Rating )
166 // const int rating = CollectionDB::instance()->getSongRating( tags.url().path() );
170 // for( int i = 0; i < rating / 2; ++i )
171 // s += QString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
172 // .arg( KStandardDirs::locate( "data", "amarok/images/star.png" ) )
173 // .arg( QFontMetrics( QToolTip::font() ).height() )
174 // .arg( QFontMetrics( QToolTip::font() ).height() );
176 // s += QString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
177 // .arg( KStandardDirs::locate( "data", "amarok/images/smallstar.png" ) )
178 // .arg( QFontMetrics( QToolTip::font() ).height() )
179 // .arg( QFontMetrics( QToolTip::font() ).height() );
181 // left << playlist->columnText( column );
184 // else if( column == PlaylistItem::Mood )
186 // if( !AmarokConfig::showMoodbar() )
189 // m_tags.moodbar().load();
191 // switch( tags.moodbar_const().state() )
193 // case Moodbar::JobQueued:
194 // case Moodbar::JobRunning:
195 // right << tags.prettyText( column );
196 // left << playlist->columnText( column );
199 // case Moodbar::Loaded:
201 // // Ok so this is a hack, but it works quite well.
202 // // Save an image in the user's home directory just so
203 // // it can be referenced in an <img> tag. Store which
204 // // moodbar is saved in m_moodbarURL so we don't have
205 // // to re-save it every second.
206 // left << playlist->columnText( column );
207 // QString filename = KStandardDirs::locateLocal( "data",
208 // "amarok/mood_tooltip.png" );
209 // int height = QFontMetrics( QToolTip::font() ).height() - 2;
211 // if( m_moodbarURL != tags.url().url() )
214 // = const_cast<MetaBundle&>( tags ).moodbar().draw(
215 // MOODBAR_WIDTH, height );
216 // moodbar.save( filename, "PNG", 100 );
217 // m_moodbarURL = tags.url().url();
220 // right << QString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
221 // .arg( filename ).arg( height ).arg( MOODBAR_WIDTH );
230 // else if( column == PlaylistItem::PlayCount )
232 // const int count = CollectionDB::instance()->getPlayCount( tags.url().path() );
235 // right << QString::number( count );
236 // left << playlist->columnText( column );
239 // else if( column == PlaylistItem::LastPlayed )
241 // const uint lastPlayed = CollectionDB::instance()->getLastPlay( tags.url().path() ).toTime_t();
242 // right << Amarok::verboseTimeSince( lastPlayed );
243 // left << playlist->columnText( column );
245 // else if( column == PlaylistItem::Filename && title.isEmpty() )
246 // filename = tags.prettyText( column );
247 // else if( column == PlaylistItem::Title && filename.isEmpty() )
248 // title = tags.prettyText( column );
249 // else if( column != PlaylistItem::Length )
251 // const QString tag = tags.prettyText( column );
252 // if( !tag.isEmpty() )
255 // left << playlist->columnText( column );
260 if( !filename
.isEmpty() )
262 right
.prepend( filename
);
264 // left.prepend( playlist->columnText( PlaylistItem::Filename ) );
266 else if( !title
.isEmpty() )
268 right
.prepend( title
);
270 // left.prepend( playlist->columnText( PlaylistItem::Title ) );
273 if( tags
.length() > 0 ) //special case this too, always on the bottom
276 right
<< "%9 / " + tags
.prettyLength();
278 // left << playlist->columnText( PlaylistItem::Length );
281 //NOTE it seems to be necessary to <center> each element indivdually
282 m_tooltip
+= "<center><b>Amarok</b></center><table cellpadding='2' cellspacing='2' align='center'><tr>";
284 m_tooltip
+= "%1"; //the cover gets substituted in, in tooltip()
285 m_cover
= CollectionDB::instance()->podcastImage( tags
, true );
286 if( m_cover
.isEmpty() || m_cover
.contains( "nocover" ) != -1 )
288 m_cover
= CollectionDB::instance()->albumImage( tags
, true, 150 );
289 if ( m_cover
== CollectionDB::instance()->notAvailCover() )
293 m_tooltip
+= "<td><table cellpadding='0' cellspacing='0'>";
295 if (tags
.title().isEmpty() || tags
.artist().isEmpty())
296 // no title or no artist, so we add prettyTitle
297 m_tooltip
+= QString ("<tr><td align=center colspan='2'>%1</td></tr>")
298 .arg(tags
.veryNiceTitle());
299 for( int x
= 0; x
< left
.count(); ++x
)
300 if ( !right
[x
].isEmpty() )
301 m_tooltip
+= tableRow
.arg( left
[x
] ).arg( right
[x
] );
303 m_tooltip
+= "</table></td>";
304 m_tooltip
+= "</tr></table></center>";
311 void TrackToolTip::setPos( int pos
)
320 void TrackToolTip::clear()
324 m_tooltip
= i18n( "Amarok - rediscover your music" );
325 m_tags
= MetaBundle();
326 m_tags
.setUrl( KUrl() );
331 QPair
<QString
, QRect
> TrackToolTip::toolTipText( QWidget
*, const QPoint
& ) const
333 return QPair
<QString
, QRect
>( tooltip(), QRect() );
336 void TrackToolTip::slotCoverChanged( const QString
&artist
, const QString
&album
)
338 if( artist
== m_tags
.artist() && album
== m_tags
.album() )
340 m_cover
= CollectionDB::instance()->albumImage( m_tags
, true, 150 );
341 if( m_cover
== CollectionDB::instance()->notAvailCover() )
348 void TrackToolTip::slotImageChanged( const QString
&remoteURL
)
350 PodcastEpisodeBundle peb
;
351 if( CollectionDB::instance()->getPodcastEpisodeBundle( m_tags
.url().url(), &peb
) )
353 PodcastChannelBundle pcb
;
354 if( CollectionDB::instance()->getPodcastChannelBundle( peb
.parent().url(), &pcb
) )
356 if( pcb
.imageURL().url() == remoteURL
)
358 m_cover
= CollectionDB::instance()->podcastImage( remoteURL
);
359 if( m_cover
== CollectionDB::instance()->notAvailCover() )
369 void TrackToolTip::slotUpdate( const QString
&url
)
371 if( url
.isNull() || url
== m_tags
.url().path() )
372 setTrack( m_tags
, true );
376 TrackToolTip::slotMoodbarEvent( void )
378 // Clear this so the moodbar gets redrawn
379 m_moodbarURL
.clear();
380 // Reset the moodbar in case AlterMood has changed
381 m_tags
.moodbar().reset();
383 setTrack( m_tags
, true );
387 QString
TrackToolTip::tooltip() const
389 QString tip
= m_tooltip
;;
390 if( !m_tags
.isEmpty() )
392 if( !m_cover
.isEmpty() )
393 tip
= tip
.arg( QString( "<td><table cellpadding='0' cellspacing='0'><tr><td>"
395 "</td></tr></table></td>" ).arg( m_cover
) );
399 tip
= tip
.arg( MetaBundle::prettyLength( m_pos
/ 1000, true ) );
404 void TrackToolTip::updateWidgets()
406 Amarok::ToolTip::updateTip();
409 #include "tracktooltip.moc"