Amarok2 Systrayicon

Leo Franchi lfranchi at gmail.com
Wed Jan 9 00:02:02 UTC 2008


On Jan 8, 2008, at 5:33 PM, Daniel Pelzl wrote:

> HI!
>
>
> Is it possible to give a helping hand to the amarok2 project?
> I was bored and so started working on the systrayicon.cpp and .h
> The Play and Pause Overlay works as well as the middle mouse button.
> You can have a look if you like.
> I have attached the files.
> Additionally you have to  copy the b_play.png and b_pause.png to
> /amarok/images

> Little problem is the icon flickering but I just found out that its a
> bug in qt and  should be fixed with qt4.4
>
> You are wondering why I  do this?
> Because it makes fun using amarok and I want amarok2 to come out as  
> soon
> as possible ;)
>

First of all, let me say thanks! We are always in need of developers,  
and are especially in need right now for amarok2, as so much still has  
to be done.

next... it would be great if you could submit patches as diffs to the  
amarok source. it makes it much easier for us to test them out. you  
can make a patch fromt he svn checkout itself by just entering "svn  
diff" and saving the output to a file.

you should also come hang out with us on #amarok at irc.freenode.net,  
where we hang out, if you want to toss around ideas or ask questions  
(my nick is lfranchi).

so to conclude, we very much welcome any patches and additions that  
you make, and i await your svn diff to take a deeper look!

leo

> If it would be possible I would like to complete the systrayicon or do
> other things
>
> Greets Daniel
>
>
>
> //
> // AmarokSystray
> //
> // Contributors: Stanislav Karchebny <berkus at users.sf.net>, (C) 2003
> //               berkus, mxcl, eros, eean
> //
> // Copyright: like rest of Amarok
> //
>
> #include "systray.h"
>
> #include "amarok.h"
> #include "amarokconfig.h"
> #include "debug.h"
> #include "enginecontroller.h"
> #include "playlist/PlaylistModel.h"
> #include "meta/Meta.h"
> #include "TheInstances.h"
>
> #include <KAction>
> #include <KApplication>
> #include <KIconEffect>
> #include <KLocale>
> #include <KMenu>
> #include <KStandardDirs>
>
> #include <QEvent>
> #include <QMouseEvent>
> #include <QTimerEvent>
> #include <QPainter>
>
>
> namespace Amarok
> {
>    static QPixmap
>    loadOverlay( const char *iconName )
>    {
>    	QPixmap tmpPixmap = QPixmap( KStandardDirs::locate( "data",  
> QString( "amarok/images/b_%1.png" ).arg( iconName ) ),  
> "PNG" ).scaled( 10, 10, Qt::IgnoreAspectRatio,  
> Qt::SmoothTransformation );
>        return tmpPixmap;
>    }
>
>    static QPixmap saveIconInPixmap( QIcon *icon )
>    {
>    	QPixmap tempPixmap = icon->pixmap(64,64);
>    	return tempPixmap;
>    }
>
>    static QPixmap makeGrayIcon( QPixmap *iconSave )
>    {
>    	QPixmap tempPixmap = *iconSave;
>    	KIconEffect::semiTransparent( tempPixmap );
>        return tempPixmap;
>    }
>    	
> }
>
>
> Amarok::TrayIcon::TrayIcon( QWidget *playerWidget )
>        : KSystemTrayIcon( playerWidget )
>        , EngineObserver( EngineController::instance() )
>        , trackLength( 0 )
>        , mergeLevel( -1 )
>        , overlay( 0 )
>        , blinkTimerID( 0 )
>        , overlayVisible( false )
>        , m_lastFmMode( false )
> {
>    KActionCollection* const ac = Amarok::actionCollection();
>
>    contextMenu()->addAction( ac->action( "prev"       ) );
>    contextMenu()->addAction( ac->action( "play_pause" ) );
>    contextMenu()->addAction( ac->action( "play_pause" ) );
>    contextMenu()->addAction( ac->action( "next"       ) );
>
>    //seems to be necessary
>    QAction *quit = actionCollection()->action( "file_quit" );
>    quit->disconnect();
>    connect( quit, SIGNAL(activated()), kapp, SLOT(quit()) );
>
>    baseIcon     = KSystemTrayIcon::loadIcon( "amarok" );
>    playOverlay  = Amarok::loadOverlay( "play" );
>    pauseOverlay = Amarok::loadOverlay( "pause" );
>    overlayVisible = false;
>
>    iconSavePixmap = saveIconInPixmap(&baseIcon);
>    grayedIcon = makeGrayIcon(&iconSavePixmap);
>
>    connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
>             this,  
> SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
>
>    //paintIcon();
>    setIcon( baseIcon );
> }
>
> void
> Amarok::TrayIcon::iconActivated ( QSystemTrayIcon::ActivationReason  
> reason )
> {
>    switch( reason )
>    {
>    case QSystemTrayIcon::MiddleClick:
>        EngineController::instance()->playPause();
>    }
> }
>
> bool
> Amarok::TrayIcon::event( QEvent *e )
> {
>    DEBUG_BLOCK
>        debug() << "Event type: " << e->type();
>    switch( e->type() )
>    {
>    case QEvent::DragEnter:
>        debug() << "QEvent::DragEnter";
>        #define e static_cast<QDragEnterEvent*>(e)
>        {
>            e->setAccepted( KUrl::List::canDecode( e->mimeData() ) );
>            break;
>        }
>        #undef e
>
>    case QEvent::Drop:
>        debug() << "QEvent::Drop";
>        #define e static_cast<QDropEvent*>(e)
>        {
>            KUrl::List list = KUrl::List::fromMimeData( e- 
> >mimeData() );
>            if( !list.isEmpty() )
>            {
>                KMenu *popup = new KMenu;
>                popup->addAction( KIcon( "list-add-amarok" ),  
> i18n( "&Append to Playlist" ), this, SLOT( appendDrops() ) );
>                popup->addAction( KIcon( "list-add-amarok" ),  
> i18n( "Append && &Play" ), this, SLOT( appendAndPlayDrops() ) );
>                if( The::playlistModel()->activeRow() >= 0 )
>                    popup->addAction( KIcon( "go-next-amarok" ),  
> i18n( "&Queue Track" ), this, SLOT( queueDrops() ) );
>
>                popup->addSeparator();
>                popup->addAction( i18n( "&Cancel" ) );
>                popup->exec( e->pos() );
>            }
>            break;
>        }
>        #undef e
>
>    case QEvent::Wheel:
>        debug() << "QEvent::Wheel";
>        #define e static_cast<QWheelEvent*>(e)
>        if( e->modifiers() == Qt::ControlModifier )
>        {
>            const bool up = e->delta() > 0;
>            if( up ) EngineController::instance()->previous();
>            else     EngineController::instance()->next();
>            break;
>        }
>        else if( e->modifiers() == Qt::ShiftModifier )
>        {
>            EngineController::instance()->seekRelative( (e->delta() /  
> 120) * 5000 ); // 5 seconds for keyboard seeking
>            break;
>        }
>        else
>            EngineController::instance()->increaseVolume( e- 
> >delta() / Amarok::VOLUME_SENSITIVITY );
>
>        e->accept();
>        #undef e
>        break;
>
>    case QEvent::Timer:
>        debug() << "QEvent::Timer";
>        if( static_cast<QTimerEvent*>(e)->timerId() != blinkTimerID )
>            return KSystemTrayIcon::event( e );
>
>        // if we're playing, blink icon
>        if ( overlay == &playOverlay )
>        {
>            overlayVisible = !overlayVisible;
>            paintIcon( mergeLevel, true );
>        }
>
>        break;
>
>    /*case QEvent::MouseButtonPress:
>        debug() << "QEvent::MouseButtonPress";
>        if( static_cast<QMouseEvent*>(e)->button() == Qt::MidButton )
>        {
>            EngineController::instance()->playPause();
>
>            return true;
>        }
> */
>        //else FALL THROUGH
>
>    default:
>        debug() << "QEvent:: fall through";
>        return QSystemTrayIcon::event( e );
>    }
>    return true;
> }
>
> void
> Amarok::TrayIcon::engineStateChanged( Engine::State state,  
> Engine::State /*oldState*/ )
> {
>    // stop timer
>    if ( blinkTimerID )
>    {
>        killTimer( blinkTimerID );
>        blinkTimerID = 0;
>    }
>    // draw overlay
>    overlayVisible = true;
>
>    // draw the right overlay for each state
>    switch( state )
>    {
>    case Engine::Paused:
>        overlay = &pauseOverlay;
>        paintIcon( mergeLevel, true );
>        break;
>
>    case Engine::Playing:
>        overlay = &playOverlay;
>        if( AmarokConfig::animateTrayIcon() )
>           blinkTimerID = startTimer( 1500 );  // start 'blink' timer
>
>        paintIcon( mergeLevel, true ); // repaint the icon
>        break;
>
>    case Engine::Empty:
>        overlayVisible = false;
>        paintIcon( -1, true ); // repaint the icon
>                               // fall through to default:
>    default:
>        setLastFm( false );
>    }
> }
>
> void
> Amarok::TrayIcon::engineNewMetaData( const QHash<qint64, QString>  
> &newMetaData, bool trackChanged )
> {
>    Q_UNUSED( trackChanged )
>    Q_UNUSED( newMetaData )
>    Meta::TrackPtr track = EngineController::instance()- 
> >currentTrack();
>    if( !track )
>        return;
>    trackLength = track->length() * 1000;
>    setLastFm( track->type() == "stream/lastfm" );
> }
>
> void
> Amarok::TrayIcon::engineTrackPositionChanged( long position, bool / 
> *userSeek*/ )
> {
>    //mergeLevel = trackLength ? ((iconSavePixmap.height() + 1) *  
> position) / trackLength : -1;
>    //paintIcon( mergeLevel );
>    //mergeLevel = position / trackLength;
>    //debug() << "Position= " << position;
>    //debug() << "Tracklength= " << trackLength;
> }
>
> void
> Amarok::TrayIcon::engineTrackLengthChanged( long seconds )
> {
>    //debug() << "Tracklaenge= " << seconds;
> }
>
> void
> Amarok::TrayIcon::paletteChange( const QPalette & op )
> {
> /*
>    if ( palette().active().highlight() == op.active().highlight() ||  
> alternateIcon.isNull() )
>        return;
>
>    alternateIcon.resize( 0, 0 );
>    paintIcon( mergeLevel, true );
> */
> }
>
> void
> Amarok::TrayIcon::paintIcon( int mergePixels, bool force )
> {
>    // skip redrawing the same pixmap
>    static int mergePixelsCache = 0;
>    if ( mergePixels == mergePixelsCache && !force )
>         return;
>    mergePixelsCache = mergePixels;
>
>    if ( mergePixels < 0 )
>    {
>    	return blendOverlay( iconSavePixmap );
>    }
>    // make up the grayed icon
> /*    if ( grayedIcon.isNull() )
>    {
>        QImage tmpTrayIcon = baseIcon.convertToImage();
>        KIconEffect::semiTransparent( tmpTrayIcon );
>        grayedIcon = tmpTrayIcon;
>    }*/
>
>    // make up the alternate icon (use hilight color but more  
> saturated)
>    if ( alternateIcon.isNull() )
>    {
>        #if 0
>        QImage tmpTrayIcon = baseIcon.convertToImage();
>        // eros: this looks cool with dark red blue or green but  
> sucks with
>        // other colors (such as kde default's pale pink..). maybe  
> the effect
>        // or the blended color has to be changed..
>        QColor saturatedColor = palette().active().highlight();
>        int hue, sat, value;
>        saturatedColor.getHsv( &hue, &sat, &value );
>        saturatedColor.setHsv( hue, sat > 200 ? 200 : sat, value <  
> 100 ? 100 : value );
>        KIconEffect::colorize( tmpTrayIcon, saturatedColor/* Qt::blue  
> */, 0.9 );
>        alternateIcon = tmpTrayIcon;
>        #endif
>    }
>
>    if ( mergePixels >= alternateIcon.height() ) {}
>        //return blendOverlay( grayedIcon );
>    if ( mergePixels == 0 ) {}
>        //return blendOverlay( alternateIcon );
>
>    // mix [ grayed <-> colored ] icons
>    QPixmap tmpTrayPixmap = alternateIcon;
>    copyBlt( &tmpTrayPixmap, 0,0, &grayedIcon, 0,0,
>            alternateIcon.width(), mergePixels>0 ? mergePixels-1 : 0 );
>    blendOverlay( tmpTrayPixmap );
> }
>
> void
> Amarok::TrayIcon::blendOverlay( QPixmap &sourcePixmap )
> {
>    if ( !overlayVisible || !overlay || overlay->isNull() )
>    {
>        baseIcon = QIcon( sourcePixmap );
>        setIcon ( baseIcon );
>        return;
>    }
>    // here comes the tricky part.. no kdefx functions are helping  
> here.. :-(
>    // we have to blend pixmaps with different sizes (blending will  
> be done in
>    // the bottom-left corner of source pixmap with a smaller overlay  
> pixmap)
>    int opW = overlay->width(),
>        opH = overlay->height(),
>        opX = 1,
>        opY = sourcePixmap.height() - opH;
>
>    QPixmap sPixmap = sourcePixmap;
>    QPainter *painter = new QPainter(&sPixmap);
>    painter->drawPixmap( 1 , sourcePixmap.height() - opH , *overlay );
>    painter->end();
>    painter->~QPainter();
> /*
>    // get the rectangle where blending will take place
>    //QPixmap sourceCropped( opW, opH, sourcePixmap.depth() );
>    QPixmap sourceCropped( opW, opH );
>    copyBlt( &sourceCropped, 0,0, &sourcePixmap, opX,opY, opW,opH );
>
>    // blend the overlay image over the cropped rectangle
>    QImage blendedImage = sourceCropped.toImage();
>    QImage overlayImage = overlay->toImage();
>    KIconEffect::overlay( blendedImage, overlayImage );
>    sourceCropped.fromImage( blendedImage );
>
>    // put back the blended rectangle to the original image
>    QPixmap sourcePixmapCopy = sourcePixmap;
>    copyBlt( &sourcePixmapCopy, opX,opY, &sourceCropped, 0,0,  
> opW,opH );
>
> */
>    baseIcon = QIcon( sPixmap );
>    setIcon ( baseIcon );
>    return;
>
> }
>
> void
> Amarok::TrayIcon::setLastFm( bool lastFmActive )
> {
>    if( lastFmActive == m_lastFmMode ) return;
>
>    static int separatorId = 0;
>
>    KActionCollection* const ac = Amarok::actionCollection();
>    if( ac->action( "ban" ) == 0 ) return; //if the  
> LastFm::Controller doesn't exist yet
>
>    if( lastFmActive )
>    {
>        contextMenu()->removeAction( ac->action( "play_pause" ) );
>        // items are inserted in reverse order!
>        contextMenu()->addAction( ac->action( "ban" ) );
>        contextMenu()->addAction( ac->action( "love" ) );
>        contextMenu()->addAction( ac->action( "skip" ) );
>        contextMenu()->addSeparator();
>
>        m_lastFmMode = true;
>    }
>    else
>    {
>
>        contextMenu()->addAction( ac->action( "play_pause" ) );
>        // items are inserted in reverse order!
>        contextMenu()->removeAction( ac->action( "ban" ) );
>        contextMenu()->removeAction( ac->action( "love" ) );
>        contextMenu()->removeAction( ac->action( "skip" ) );
>
>        //contextMenu()->removeSeparator();
>        m_lastFmMode = false;
>   }
> }
>
> //
> // AmarokSystray
> //
> // Author: Stanislav Karchebny <berkus at users.sf.net>, (C) 2003
> //
> // Copyright: like rest of Amarok
> //
>
> #ifndef AMAROKSYSTRAY_H
> #define AMAROKSYSTRAY_H
>
> #include "engineobserver.h" //baseclass
>
> #include <ksystemtrayicon.h>
>
> #include <QPixmap>
>
> class QEvent;
>
> class App;
>
> namespace Amarok {
>
> class TrayIcon : public KSystemTrayIcon, public EngineObserver
> {
>    Q_OBJECT
> public:
>    TrayIcon( QWidget* );
>    friend class ::App;
>
> protected:
>    // reimpl from engineobserver
>    virtual void engineStateChanged( Engine::State state,  
> Engine::State oldState = Engine::Empty );
>    virtual void engineNewMetaData( const QHash<qint64, QString>  
> &newMetaData, bool trackChanged );
>    virtual void engineTrackPositionChanged( long position, bool / 
> *userSeek*/ );
>    // get notified of 'highlight' color change
>    virtual void paletteChange( const QPalette & oldPalette );
>
>    virtual void engineTrackLengthChanged( long seconds );
> private slots:
>    void iconActivated ( QSystemTrayIcon::ActivationReason reason );
>
> private:
>    virtual bool event( QEvent *e );
>    void setLastFm( bool );
>
>    // repaints trayIcon showing progress (and overlay if present)
>    void paintIcon( int mergePixels = -1, bool force = false );
>    // blend an overlay icon over 'sourcePixmap' and repaint trayIcon
>    void blendOverlay( QPixmap &sourcePixmap );
>
>    long trackLength, mergeLevel;
>    QIcon baseIcon;
>    QPixmap grayedIcon, alternateIcon;
>    QPixmap playOverlay, pauseOverlay;
>    QPixmap iconSavePixmap;
>    QPixmap *overlay;   // the current overlay (may be NULL)
>    int blinkTimerID;   // timer ID returned by QObject::startTimer()
>    bool overlayVisible;// used for blinking / hiding overlay
>    /** whether the last.fm icons are visible **/
>    bool m_lastFmMode;
> };
>
> }
>
> #endif
>
> < 
> b_pause 
> .png><b_play.png>_______________________________________________
> Amarok mailing list
> Amarok at kde.org
> https://mail.kde.org/mailman/listinfo/amarok




More information about the Amarok mailing list