4 // Contributors: Stanislav Karchebny <berkus@users.sf.net>, (C) 2003
5 // berkus, mxcl, eros, eean
7 // Copyright: like rest of Amarok
13 #include "amarokconfig.h"
14 #include "enginecontroller.h"
15 #include "meta/meta.h"
18 #include <KApplication>
19 #include <KIconEffect>
21 #include <KStandardDirs>
25 #include <QMouseEvent>
27 #include <QTimerEvent>
33 loadOverlay( const char *iconName
)
35 return QImage( KStandardDirs::locate( "data", QString( "amarok/images/b_%1.png" ).arg( iconName
) ), "PNG" )
36 .scaled( 10, 10, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
41 Amarok::TrayIcon::TrayIcon( QWidget
*playerWidget
)
42 : KSystemTrayIcon( playerWidget
)
43 , EngineObserver( EngineController::instance() )
48 , overlayVisible( false )
49 , m_lastFmMode( false )
51 KActionCollection
* const ac
= Amarok::actionCollection();
53 //setAcceptDrops( true );
55 contextMenu()->addAction( ac
->action( "prev" ) );
56 contextMenu()->addAction( ac
->action( "play_pause" ) );
57 contextMenu()->addAction( ac
->action( "play_pause" ) );
58 contextMenu()->addAction( ac
->action( "next" ) );
60 //seems to be necessary
61 QAction
*quit
= actionCollection()->action( "file_quit" );
63 connect( quit
, SIGNAL(activated()), kapp
, SLOT(quit()) );
65 baseIcon
= KSystemTrayIcon::loadIcon( "amarok" );
66 playOverlay
= Amarok::loadOverlay( "play" );
67 pauseOverlay
= Amarok::loadOverlay( "pause" );
68 overlayVisible
= false;
75 Amarok::TrayIcon::event( QEvent
*e
)
81 case QEvent::DragEnter
:
82 //return Amarok::genericEventHandler( this, e );
85 if( static_cast<QTimerEvent
*>(e
)->timerId() != blinkTimerID
)
86 return KSystemTrayIcon::event( e
);
88 // if we're playing, blink icon
89 if ( overlay
== &playOverlay
)
91 overlayVisible
= !overlayVisible
;
92 paintIcon( mergeLevel
, true );
97 case QEvent::MouseButtonPress
:
98 if( static_cast<QMouseEvent
*>(e
)->button() == Qt::MidButton
)
100 EngineController::instance()->playPause();
108 return KSystemTrayIcon::event( e
);
113 Amarok::TrayIcon::engineStateChanged( Engine::State state
, Engine::State
/*oldState*/ )
118 killTimer( blinkTimerID
);
122 overlayVisible
= true;
124 // draw the right overlay for each state
128 overlay
= &pauseOverlay
;
129 paintIcon( mergeLevel
, true );
132 case Engine::Playing
:
133 overlay
= &playOverlay
;
134 if( AmarokConfig::animateTrayIcon() )
135 blinkTimerID
= startTimer( 1500 ); // start 'blink' timer
137 paintIcon( mergeLevel
, true ); // repaint the icon
141 overlayVisible
= false;
142 paintIcon( -1, true ); // repaint the icon
143 // fall through to default:
150 Amarok::TrayIcon::engineNewMetaData( const QHash
<qint64
, QString
> &newMetaData
, bool trackChanged
)
152 Q_UNUSED( trackChanged
)
153 Q_UNUSED( newMetaData
)
154 Meta::TrackPtr track
= EngineController::instance()->currentTrack();
157 trackLength
= track
->length() * 1000;
158 setLastFm( track
->type() == "stream/lastfm" );
162 Amarok::TrayIcon::engineTrackPositionChanged( long position
, bool /*userSeek*/ )
165 mergeLevel = trackLength ? ((baseIcon.height() + 1) * position) / trackLength : -1;
166 paintIcon( mergeLevel );
172 Amarok::TrayIcon::paletteChange( const QPalette
& op
)
175 if ( palette().active().highlight() == op.active().highlight() || alternateIcon.isNull() )
178 alternateIcon.resize( 0, 0 );
179 paintIcon( mergeLevel, true );
184 Amarok::TrayIcon::paintIcon( int mergePixels
, bool force
)
186 // skip redrawing the same pixmap
187 static int mergePixelsCache
= 0;
188 if ( mergePixels
== mergePixelsCache
&& !force
)
190 mergePixelsCache
= mergePixels
;
192 if ( mergePixels
< 0 ) {}
193 //return blendOverlay( baseIcon );
195 // make up the grayed icon
196 if ( grayedIcon
.isNull() )
198 //QImage tmpTrayIcon = baseIcon.convertToImage();
199 //KIconEffect::semiTransparent( tmpTrayIcon );
200 //grayedIcon = tmpTrayIcon;
203 // make up the alternate icon (use hilight color but more saturated)
204 if ( alternateIcon
.isNull() )
207 QImage tmpTrayIcon
= baseIcon
.convertToImage();
208 // eros: this looks cool with dark red blue or green but sucks with
209 // other colors (such as kde default's pale pink..). maybe the effect
210 // or the blended color has to be changed..
211 QColor saturatedColor
= palette().active().highlight();
213 saturatedColor
.getHsv( &hue
, &sat
, &value
);
214 saturatedColor
.setHsv( hue
, sat
> 200 ? 200 : sat
, value
< 100 ? 100 : value
);
215 KIconEffect::colorize( tmpTrayIcon
, saturatedColor
/* Qt::blue */, 0.9 );
216 alternateIcon
= tmpTrayIcon
;
220 if ( mergePixels
>= alternateIcon
.height() ) {}
221 //return blendOverlay( grayedIcon );
222 if ( mergePixels
== 0 ) {}
223 //return blendOverlay( alternateIcon );
225 // mix [ grayed <-> colored ] icons
226 QPixmap tmpTrayPixmap
= alternateIcon
;
227 copyBlt( &tmpTrayPixmap
, 0,0, &grayedIcon
, 0,0,
228 alternateIcon
.width(), mergePixels
>0 ? mergePixels
-1 : 0 );
229 blendOverlay( tmpTrayPixmap
);
233 Amarok::TrayIcon::blendOverlay( QPixmap
&sourcePixmap
)
236 if ( !overlayVisible
|| !overlay
|| overlay
->isNull() )
237 return setPixmap( sourcePixmap
); // @since 3.2
239 // here comes the tricky part.. no kdefx functions are helping here.. :-(
240 // we have to blend pixmaps with different sizes (blending will be done in
241 // the bottom-left corner of source pixmap with a smaller overlay pixmap)
242 int opW
= overlay
->width(),
243 opH
= overlay
->height(),
245 opY
= sourcePixmap
.height() - opH
;
247 // get the rectangle where blending will take place
248 QPixmap
sourceCropped( opW
, opH
, sourcePixmap
.depth() );
249 copyBlt( &sourceCropped
, 0,0, &sourcePixmap
, opX
,opY
, opW
,opH
);
251 //speculative fix for a bactrace we received
252 //crash was in covertToImage() somewhere in this function
253 if( sourceCropped
.isNull() )
254 return setPixmap( sourcePixmap
);
256 // blend the overlay image over the cropped rectangle
257 QImage blendedImage
= sourceCropped
.convertToImage();
258 QImage overlayImage
= overlay
->convertToImage();
259 KIconEffect::overlay( blendedImage
, overlayImage
);
260 sourceCropped
.convertFromImage( blendedImage
);
262 // put back the blended rectangle to the original image
263 QPixmap sourcePixmapCopy
= sourcePixmap
;
264 copyBlt( &sourcePixmapCopy
, opX
,opY
, &sourceCropped
, 0,0, opW
,opH
);
266 setPixmap( sourcePixmapCopy
); // @since 3.2
271 Amarok::TrayIcon::setLastFm( bool lastFmActive
)
273 if( lastFmActive
== m_lastFmMode
) return;
275 static int separatorId
= 0;
277 KActionCollection
* const ac
= Amarok::actionCollection();
278 if( ac
->action( "ban" ) == 0 ) return; //if the LastFm::Controller doesn't exist yet
282 contextMenu()->removeAction( ac
->action( "play_pause" ) );
283 // items are inserted in reverse order!
284 contextMenu()->addAction( ac
->action( "ban" ) );
285 contextMenu()->addAction( ac
->action( "love" ) );
286 contextMenu()->addAction( ac
->action( "skip" ) );
287 contextMenu()->addSeparator();
294 contextMenu()->addAction( ac
->action( "play_pause" ) );
295 // items are inserted in reverse order!
296 contextMenu()->removeAction( ac
->action( "ban" ) );
297 contextMenu()->removeAction( ac
->action( "love" ) );
298 contextMenu()->removeAction( ac
->action( "skip" ) );
300 //contextMenu()->removeSeparator();
301 m_lastFmMode
= false;