1
/****************************************************************************************
2
 * Copyright (c) 2003 Stanislav Karchebny <berkus@users.sf.net>                         *
3
 * Copyright (c) 2003 Max Howell <max.howell@methylblue.com>                            *
4
 * Copyright (c) 2004 Enrico Ros <eros.kde@email.it>                                    *
5
 * Copyright (c) 2006 Ian Monroe <ian@monroe.nu>                                        *
6
 * Copyright (c) 2009,2010 Kevin Funk <krf@electrostorm.net>                            *
7
 * Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org>                            *
8
 *                                                                                      *
9
 * This program is free software; you can redistribute it and/or modify it under        *
10
 * the terms of the GNU General Public License as published by the Free Software        *
11
 * Foundation; either version 2 of the License, or (at your option) any later           *
12
 * version.                                                                             *
13
 *                                                                                      *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
15
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
16
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
17
 *                                                                                      *
18
 * You should have received a copy of the GNU General Public License along with         *
19
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
20
 ****************************************************************************************/
21
22
#include "TrayIcon.h"
23
24
#include "core/support/Amarok.h"
25
#include "core/support/Debug.h"
26
#include "EngineController.h"
27
#include "amarokconfig.h"
28
#include "GlobalCurrentTrackActions.h"
29
#include "core/capabilities/CurrentTrackActionsCapability.h"
30
#include "playlist/PlaylistActions.h"
31
#include "SvgHandler.h"
32
#include <KAboutData>
33
#include <KAction>
34
#include <KCmdLineArgs>
35
#include <KLocale>
36
#include <KMenu>
37
#include <KStandardDirs>
38
39
#include <QAction>
40
#include <QFontMetrics>
41
#include <QPixmap>
42
#include <QToolTip>
43
44
Amarok::TrayIcon::TrayIcon( QObject *parent )
45
        : KStatusNotifierItem( parent )
46
        , Engine::EngineObserver( The::engineController() )
47
        , m_separator( 0 )
48
{
49
    DEBUG_BLOCK
50
51
    PERF_LOG( "Beginning TrayIcon Constructor" );
52
    KActionCollection* const ac = Amarok::actionCollection();
53
54
    setStatus( KStatusNotifierItem::Active );
55
56
    PERF_LOG( "Before adding actions" );
57
58
#ifdef Q_WS_MAC
59
    // Add these functions to the dock icon menu in OS X
60
    extern void qt_mac_set_dock_menu(QMenu *);
61
    qt_mac_set_dock_menu( contextMenu() );
62
    contextMenu()->addAction( ac->action( "playlist_playmedia" ) );
63
    contextMenu()->addSeparator();
64
#endif
65
66
    contextMenu()->addAction( ac->action( "prev"       ) );
67
    contextMenu()->addAction( ac->action( "play_pause" ) );
68
    contextMenu()->addAction( ac->action( "stop"       ) );
69
    contextMenu()->addAction( ac->action( "next"       ) );
70
    contextMenu()->setObjectName( "TrayIconContextMenu" );
71
72
    PERF_LOG( "Adding system tray icon" );
73
74
    setIconByName( "amarok" );
75
76
    setupToolTip( true );
77
78
    connect( this, SIGNAL( scrollRequested( int, Qt::Orientation ) ), SLOT( slotScrollRequested(int, Qt::Orientation) ) );
79
    connect( this, SIGNAL( secondaryActivateRequested( const QPoint & ) ), SLOT( slotActivated() ) );
80
}
81
82
void
83
Amarok::TrayIcon::setupToolTip( bool updateIcon )
84
{
85
    if( m_track )
86
    {
87
        setToolTipTitle( The::engineController()->prettyNowPlaying() );
88
89
        // check if we really need to update the icon (performance tweak)
90
        if( updateIcon )
91
        {
92
            if( m_track->album() && m_track->album()->hasImage() )
93
            {
94
                QPixmap image = The::svgHandler()->imageWithBorder( m_track->album(), KIconLoader::SizeLarge, 5 );
95
                setToolTipIconByPixmap( image );
96
            }
97
            else
98
            {
99
                setToolTipIconByName( "amarok" );
100
            }
101
        }
102
103
        QStringList tooltip;
104
105
        QString volume;
106
        if ( The::engineController()->isMuted() )
107
        {
108
            volume = i18n( "Muted" );
109
        }
110
        else
111
        {
112
            volume = QString( "%1%" ).arg( The::engineController()->volume() );
113
        }
114
        tooltip << QString("<i>%1: %2</i>").arg( i18n( "Volume" ) ).arg( volume );
115
116
        const float score = m_track->score();
117
        if( score > 0.f )
118
        {
119
            tooltip << QString( "%1: %2" ).arg( i18n( "Score" ) ).arg( QString::number( score, 'f', 2 ) );
120
        }
121
122
        const int rating = m_track->rating();
123
        if( rating > 0 )
124
        {
125
            QString stars;
126
            for( int i = 0; i < rating / 2; ++i )
127
                stars += QString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
128
                        .arg( KStandardDirs::locate( "data", "amarok/images/star.png" ) )
129
                        .arg( QFontMetrics( QToolTip::font() ).height() )
130
                        .arg( QFontMetrics( QToolTip::font() ).height() );
131
            if( rating % 2 )
132
                stars += QString( "<img src=\"%1\" height=\"%2\" width=\"%3\">" )
133
                        .arg( KStandardDirs::locate( "data", "amarok/images/smallstar.png" ) )
134
                        .arg( QFontMetrics( QToolTip::font() ).height() )
135
                        .arg( QFontMetrics( QToolTip::font() ).height() );
136
137
            tooltip << QString( "%1: %2" ).arg( i18n( "Rating" ) ).arg( stars );
138
        }
139
140
        const int count = m_track->playCount();
141
        if( count > 0 )
142
        {
143
            tooltip << QString( "%1: %2" ).arg( i18n( "Play Count" ) ).arg( QString::number( count ) );
144
        }
145
146
        const uint lastPlayed = m_track->lastPlayed();
147
        tooltip << QString( "%1: %2" ).arg( i18n( "Last played" ) ).arg( Amarok::verboseTimeSince( lastPlayed ) );
148
149
        setToolTipSubTitle( tooltip.join("<br>") );
150
    }
151
    else
152
    {
153
        setToolTipIconByName( "amarok" );
154
        setToolTipTitle( KCmdLineArgs::aboutData()->programName() );
155
        setToolTipSubTitle( The::engineController()->prettyNowPlaying() );
156
    }
157
}
158
159
void
160
Amarok::TrayIcon::slotScrollRequested( int delta, Qt::Orientation orientation )
161
{
162
    Q_UNUSED( orientation )
163
164
    The::engineController()->increaseVolume( delta / Amarok::VOLUME_SENSITIVITY );
165
}
166
167
void
168
Amarok::TrayIcon::engineStateChanged( Phonon::State state, Phonon::State /*oldState*/ )
169
{
170
    Meta::TrackPtr track = The::engineController()->currentTrack();
171
172
    switch( state )
173
    {
174
        case Phonon::PlayingState:
175
            if ( m_track )
176
            {
177
                unsubscribeFrom( m_track );
178
                unsubscribeFrom( m_track->album() );
179
            }
180
            m_track = track;
181
            if ( track )
182
            {
183
                subscribeTo( track );
184
                subscribeTo( track->album() );
185
            }
186
187
            setOverlayIconByName( "media-playback-start" );
188
            setupMenu();
189
            break;
190
191
        case Phonon::StoppedState:
192
            m_track = 0;
193
194
            setOverlayIconByName( QString() );
195
            setupMenu(); // remove custom track actions on stop
196
            break;
197
198
        case Phonon::PausedState:
199
            setOverlayIconByName( "media-playback-pause" );
200
            break;
201
202
        case Phonon::LoadingState:
203
        case Phonon::ErrorState:
204
        case Phonon::BufferingState:
205
            setOverlayIconByName( QString() );
206
            break;
207
    }
208
209
    setupToolTip( true );
210
}
211
212
void
213
Amarok::TrayIcon::engineNewTrackPlaying()
214
{
215
    m_track = The::engineController()->currentTrack();
216
217
    setupToolTip( true );
218
    setupMenu();
219
}
220
221
void
222
Amarok::TrayIcon::metadataChanged( Meta::TrackPtr track )
223
{
224
    Q_UNUSED( track )
225
226
    setupToolTip( false );
227
    setupMenu();
228
}
229
230
void
231
Amarok::TrayIcon::metadataChanged( Meta::AlbumPtr album )
232
{
233
    Q_UNUSED( album )
234
235
    setupToolTip( true );
236
    setupMenu();
237
}
238
239
void
240
Amarok::TrayIcon::engineVolumeChanged( int percent )
241
{
242
    Q_UNUSED( percent );
243
244
    setupToolTip( false );
245
}
246
247
void
248
Amarok::TrayIcon::engineMuteStateChanged( bool mute )
249
{
250
    Q_UNUSED( mute );
251
252
    setupToolTip( false );
253
}
254
255
void
256
Amarok::TrayIcon::setupMenu()
257
{
258
    foreach( QAction* action, m_extraActions )
259
        contextMenu()->removeAction( action );
260
261
    contextMenu()->removeAction( m_separator );
262
263
    delete m_separator;
264
265
    if( !m_track )
266
        return;
267
268
    m_extraActions.clear();
269
    foreach( QAction *action, The::globalCurrentTrackActions()->actions() )
270
        m_extraActions.append( action );
271
272
    if ( m_track->hasCapabilityInterface( Capabilities::Capability::CurrentTrackActions ) )
273
    {
274
        Capabilities::CurrentTrackActionsCapability *cac = m_track->create<Capabilities::CurrentTrackActionsCapability>();
275
        if( cac )
276
        {
277
            QList<QAction *> currentTrackActions = cac->customActions();
278
            foreach( QAction *action, currentTrackActions )
279
                m_extraActions.append( action );
280
        }
281
        delete cac;
282
    }
283
284
    if ( m_extraActions.count() > 0 )
285
    {
286
        // remove the two bottom items, so we can push them to the button again
287
        contextMenu()->removeAction( actionCollection()->action( "file_quit" ) );
288
        contextMenu()->removeAction( actionCollection()->action( "minimizeRestore" ) );
289
290
        foreach( QAction* action, m_extraActions )
291
            contextMenu()->addAction( action );
292
293
        m_separator = contextMenu()->addSeparator();
294
        // readd
295
        contextMenu()->addAction( actionCollection()->action( "minimizeRestore" ) );
296
        contextMenu()->addAction( actionCollection()->action( "file_quit" ) );
297
    }
298
}
299
300
void
301
Amarok::TrayIcon::slotActivated()
302
{
303
    The::engineController()->playPause();
304
}
305
306
#include "TrayIcon.moc"