/*
nowlisteningplugin . cpp
Kopete Now Listening To plugin
Copyright ( c ) 2002 , 2003 , 2004 by Will Stephenson < will @ stevello . free - online . co . uk >
Copyright ( c ) 2005 - 2006 by Micha ë l Larouche < michael . larouche @ kdemail . net >
Kopete ( c ) 2002 - 2006 by the Kopete developers < kopete - devel @ kde . org >
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
# include <tqtimer.h>
# include <tqstringlist.h>
# include <tqregexp.h>
# include <kdebug.h>
# include <kgenericfactory.h>
# include <kapplication.h>
# include <dcopclient.h>
# include <kaction.h>
# include "config.h"
# include "kopetechatsessionmanager.h"
# include "kopetemetacontact.h"
# include "kopetecontact.h"
# include "kopetecommandhandler.h"
# include "kopeteaccount.h"
# include "kopeteprotocol.h"
# include "kopeteaccountmanager.h"
# include "nowlisteningconfig.h"
# include "nowlisteningplugin.h"
# include "nlmediaplayer.h"
# include "nlkscd.h"
# include "nlnoatun.h"
# include "nljuk.h"
# include "nlamarok.h"
# include "nlkaffeine.h"
# include "nowlisteningguiclient.h"
# if defined TQ_WS_X11 && !defined K_WS_TQTONLY && defined HAVE_XMMS
# include "nlxmms.h"
# endif
class NowListeningPlugin : : Private
{
public :
Private ( ) : m_currentMediaPlayer ( 0L ) , m_client ( 0L ) , m_currentChatSession ( 0L ) , m_currentMetaContact ( 0L ) ,
advertTimer ( 0L )
{ }
// abstracted media player interfaces
TQPtrList < NLMediaPlayer > m_mediaPlayerList ;
NLMediaPlayer * m_currentMediaPlayer ;
// Needed for DCOP interprocess communication
DCOPClient * m_client ;
Kopete : : ChatSession * m_currentChatSession ;
Kopete : : MetaContact * m_currentMetaContact ;
// Used when using automatic advertising to know who has already gotten
// the music information
TQStringList m_musicSentTo ;
// Used when advertising to status message.
TQTimer * advertTimer ;
} ;
typedef KGenericFactory < NowListeningPlugin > NowListeningPluginFactory ;
K_EXPORT_COMPONENT_FACTORY ( kopete_nowlistening , NowListeningPluginFactory ( " kopete_nowlistening " ) )
NowListeningPlugin : : NowListeningPlugin ( TQObject * tqparent , const char * name , const TQStringList & /*args*/ )
: Kopete : : Plugin ( NowListeningPluginFactory : : instance ( ) , tqparent , name )
{
if ( pluginStatic_ )
kdDebug ( 14307 ) < < " #### " < < " Now Listening already initialized " < < endl ;
else
pluginStatic_ = this ;
d = new Private ;
kdDebug ( 14307 ) < < k_funcinfo < < endl ;
// Connection for the "/media" command (always needed)
connect ( Kopete : : ChatSessionManager : : self ( ) , TQT_SIGNAL (
chatSessionCreated ( Kopete : : ChatSession * ) ) , TQT_SLOT ( slotNewKMM (
Kopete : : ChatSession * ) ) ) ;
// If autoadvertising is on...
connect ( Kopete : : ChatSessionManager : : self ( ) ,
TQT_SIGNAL ( aboutToSend ( Kopete : : Message & ) ) ,
this ,
TQT_SLOT ( slotOutgoingMessage ( Kopete : : Message & ) ) ) ;
TQValueList < Kopete : : ChatSession * > sessions = Kopete : : ChatSessionManager : : self ( ) - > sessions ( ) ;
for ( TQValueListIterator < Kopete : : ChatSession * > it = sessions . begin ( ) ; it ! = sessions . end ( ) ; + + it )
slotNewKMM ( * it ) ;
// get a pointer to the dcop client
d - > m_client = kapp - > dcopClient ( ) ; //new DCOPClient();
// set up known media players
d - > m_mediaPlayerList . setAutoDelete ( true ) ;
d - > m_mediaPlayerList . append ( new NLKscd ( d - > m_client ) ) ;
d - > m_mediaPlayerList . append ( new NLNoatun ( d - > m_client ) ) ;
d - > m_mediaPlayerList . append ( new NLJuk ( d - > m_client ) ) ;
d - > m_mediaPlayerList . append ( new NLamaroK ( d - > m_client ) ) ;
d - > m_mediaPlayerList . append ( new NLKaffeine ( d - > m_client ) ) ;
# if defined TQ_WS_X11 && !defined K_WS_TQTONLY && HAVE_XMMS
d - > m_mediaPlayerList . append ( new NLXmms ( ) ) ;
# endif
// User has selected a specific mediaPlayer so update the currentMediaPlayer pointer.
if ( NowListeningConfig : : self ( ) - > useSpecifiedMediaPlayer ( ) )
{
updateCurrentMediaPlayer ( ) ;
}
// watch for '/media' getting typed
Kopete : : CommandHandler : : commandHandler ( ) - > registerCommand (
this ,
" media " ,
TQT_SLOT ( slotMediaCommand ( const TQString & , Kopete : : ChatSession * ) ) ,
i18n ( " USAGE: /media - Displays information on current song " ) ,
0
) ;
connect ( this , TQT_SIGNAL ( settingsChanged ( ) ) , this , TQT_SLOT ( slotSettingsChanged ( ) ) ) ;
// Advert the accounts with the current listened track.
d - > advertTimer = new TQTimer ( this ) ;
connect ( d - > advertTimer , TQT_SIGNAL ( timeout ( ) ) , this , TQT_SLOT ( slotAdvertCurrentMusic ( ) ) ) ;
d - > advertTimer - > start ( 5000 ) ; // Update every 5 seconds
}
NowListeningPlugin : : ~ NowListeningPlugin ( )
{
//kdDebug( 14307 ) << k_funcinfo << endl;
delete d ;
pluginStatic_ = 0L ;
}
void NowListeningPlugin : : slotNewKMM ( Kopete : : ChatSession * KMM )
{
new NowListeningGUIClient ( KMM , this ) ;
}
NowListeningPlugin * NowListeningPlugin : : plugin ( )
{
return pluginStatic_ ;
}
void NowListeningPlugin : : slotMediaCommand ( const TQString & args , Kopete : : ChatSession * theChat )
{
TQString advert = mediaPlayerAdvert ( ) ;
if ( advert . isEmpty ( ) )
{
// Catch no players/no track playing message case:
// Since we can't stop a message send in a plugin, add some message text to
// prevent us sending an empty message
advert = i18n ( " Message from Kopete user to another user; used when sending media information even though there are no songs playing or no media players running " , " Now Listening for Kopete - it would tell you what I am listening to, if I was listening to something on a supported media player. " ) ;
}
Kopete : : Message msg ( theChat - > myself ( ) ,
theChat - > members ( ) ,
advert + " " + args ,
Kopete : : Message : : Outbound ,
Kopete : : Message : : RichText
) ;
theChat - > sendMessage ( msg ) ;
}
void NowListeningPlugin : : slotOutgoingMessage ( Kopete : : Message & msg )
{
// Only do stuff if autoadvertising is on
if ( ! NowListeningConfig : : self ( ) - > chatAdvertising ( ) )
return ;
TQString originalBody = msg . plainBody ( ) ;
// If it is a /media message, don't process it
if ( originalBody . startsWith ( NowListeningConfig : : self ( ) - > header ( ) ) )
return ;
// What will be sent
TQString newBody ;
// Getting the list of contacts the message will be sent to to determine if at least
// one of them has never gotten the current music information.
Kopete : : ContactPtrList dest = msg . to ( ) ;
bool mustSendAnyway = false ;
for ( Kopete : : Contact * c = dest . first ( ) ; c ; c = dest . next ( ) )
{
const TQString & cId = c - > contactId ( ) ;
if ( 0 = = d - > m_musicSentTo . tqcontains ( cId ) )
{
mustSendAnyway = true ;
// The contact will get the music information so we put it in the list.
d - > m_musicSentTo . push_back ( cId ) ;
}
}
bool newTrack = newTrackPlaying ( ) ;
// We must send the music information if someone has never gotten it or the track(s)
// has changed since it was last sent.
if ( mustSendAnyway | | newTrack )
{
TQString advert = mediaPlayerAdvert ( false ) ; // false since newTrackPlaying() did the update
if ( ! advert . isEmpty ( ) )
newBody = originalBody + " <br> " + advert ;
// If we send because the information has changed since it was last sent, we must
// rebuild the list of contacts the latest information was sent to.
if ( newTrack )
{
d - > m_musicSentTo . clear ( ) ;
for ( Kopete : : Contact * c = dest . first ( ) ; c ; c = dest . next ( ) )
{
d - > m_musicSentTo . push_back ( c - > contactId ( ) ) ;
}
}
}
// If the body has been modified, change the message
if ( ! newBody . isEmpty ( ) )
{
msg . setBody ( newBody , Kopete : : Message : : RichText ) ;
}
}
void NowListeningPlugin : : slotAdvertCurrentMusic ( )
{
// Do anything when statusAdvertising is off.
if ( ! NowListeningConfig : : self ( ) - > statusAdvertising ( ) & & ! NowListeningConfig : : self ( ) - > appendStatusAdvertising ( ) )
return ;
// This slot is called every 5 seconds, so we check if we have a new track playing.
if ( newTrackPlaying ( ) )
{
TQString advert ;
TQPtrList < Kopete : : Account > accountsList = Kopete : : AccountManager : : self ( ) - > accounts ( ) ;
for ( Kopete : : Account * a = accountsList . first ( ) ; a ; a = accountsList . next ( ) )
{
/*
NOTE :
MSN status message ( personal message ) use a special tag to advert the current music playing .
So , we don ' t send the all formatted string , send a special string seperated by " ; " .
Also , do not use MSN hack in appending mode .
*/
if ( a - > protocol ( ) - > pluginId ( ) = = " MSNProtocol " & & ! NowListeningConfig : : self ( ) - > appendStatusAdvertising ( ) )
{
TQString track , artist , album , mediaList ;
bool isPlaying = false ;
if ( NowListeningConfig : : self ( ) - > useSpecifiedMediaPlayer ( ) & & d - > m_currentMediaPlayer )
{
if ( d - > m_currentMediaPlayer - > playing ( ) )
{
track = d - > m_currentMediaPlayer - > track ( ) ;
artist = d - > m_currentMediaPlayer - > artist ( ) ;
album = d - > m_currentMediaPlayer - > album ( ) ;
mediaList = track + " ; " + artist + " ; " + album ;
isPlaying = true ;
}
}
else
{
for ( NLMediaPlayer * i = d - > m_mediaPlayerList . first ( ) ; i ; i = d - > m_mediaPlayerList . next ( ) )
{
if ( i - > playing ( ) )
{
track = i - > track ( ) ;
artist = i - > artist ( ) ;
album = i - > album ( ) ;
mediaList = track + " ; " + artist + " ; " + album ;
isPlaying = true ;
}
}
}
// KDE4 TODO: Use the new status message framework, and remove this "hack".
if ( isPlaying )
{
advert = TQString ( " [Music]%1 " ) . tqarg ( mediaList ) ;
}
}
else
{
if ( NowListeningConfig : : self ( ) - > appendStatusAdvertising ( ) )
{
// Check for the now listening message in parenthesis,
// include the header to not override other messages in parenthesis.
TQRegExp statusSong ( TQString ( " \\ (%1.* \\ ) $ " ).tqarg( NowListeningConfig::header()) ) ;
// HACK: Don't keep appending the now listened song. Replace it in the status message.
advert = a - > myself ( ) - > property ( Kopete : : Global : : Properties : : self ( ) - > awayMessage ( ) ) . value ( ) . toString ( ) ;
// Remove the braces when they are no listened song.
TQString mediaAdvert = mediaPlayerAdvert ( false ) ;
if ( ! mediaAdvert . isEmpty ( ) )
{
if ( statusSong . search ( advert ) ! = - 1 )
{
advert = advert . tqreplace ( statusSong , TQString ( " (%1) " ) . tqarg ( mediaPlayerAdvert ( false ) ) ) ;
}
else
{
advert + = TQString ( " (%1) " ) . tqarg ( mediaPlayerAdvert ( false ) ) ;
}
}
else
{
advert = advert . tqreplace ( statusSong , " " ) ;
}
}
else
{
advert = mediaPlayerAdvert ( false ) ; // newTrackPlaying has done the update.
}
}
a - > setOnlineStatus ( a - > myself ( ) - > onlinetqStatus ( ) , advert ) ;
}
}
}
TQString NowListeningPlugin : : mediaPlayerAdvert ( bool update )
{
// generate message for all players
TQString message ;
if ( NowListeningConfig : : self ( ) - > useSpecifiedMediaPlayer ( ) & & d - > m_currentMediaPlayer ! = 0L )
{
buildTrackMessage ( message , d - > m_currentMediaPlayer , update ) ;
}
else
{
for ( NLMediaPlayer * i = d - > m_mediaPlayerList . first ( ) ; i ; i = d - > m_mediaPlayerList . next ( ) )
{
buildTrackMessage ( message , i , update ) ;
}
}
kdDebug ( 14307 ) < < k_funcinfo < < message < < endl ;
return message ;
}
void NowListeningPlugin : : buildTrackMessage ( TQString & message , NLMediaPlayer * player , bool update )
{
TQString perTrack = NowListeningConfig : : self ( ) - > perTrack ( ) ;
if ( update )
player - > update ( ) ;
if ( player - > playing ( ) )
{
kdDebug ( 14307 ) < < k_funcinfo < < player - > name ( ) < < " is playing " < < endl ;
if ( message . isEmpty ( ) )
message = NowListeningConfig : : self ( ) - > header ( ) ;
if ( message ! = NowListeningConfig : : self ( ) - > header ( ) ) // > 1 track playing!
message = message + NowListeningConfig : : self ( ) - > conjunction ( ) ;
message = message + substDepthFirst ( player , perTrack , false ) ;
}
}
bool NowListeningPlugin : : newTrackPlaying ( void ) const
{
if ( NowListeningConfig : : self ( ) - > useSpecifiedMediaPlayer ( ) & & d - > m_currentMediaPlayer ! = 0L )
{
d - > m_currentMediaPlayer - > update ( ) ;
if ( d - > m_currentMediaPlayer - > newTrack ( ) )
return true ;
}
else
{
for ( NLMediaPlayer * i = d - > m_mediaPlayerList . first ( ) ; i ; i = d - > m_mediaPlayerList . next ( ) )
{
i - > update ( ) ;
if ( i - > newTrack ( ) )
return true ;
}
}
return false ;
}
TQString NowListeningPlugin : : substDepthFirst ( NLMediaPlayer * player ,
TQString in , bool inBrackets ) const
{
TQString track = player - > track ( ) ;
TQString artist = player - > artist ( ) ;
TQString album = player - > album ( ) ;
TQString playerName = player - > name ( ) ;
for ( unsigned int i = 0 ; i < in . length ( ) ; i + + )
{
TQChar c = in . at ( i ) ;
//kdDebug(14307) << "Now working on:" << in << " char is: " << c << endl;
if ( c = = ' ( ' )
{
// find matching bracket
int depth = 0 ;
//kdDebug(14307) << "Looking for ')'" << endl;
for ( unsigned int j = i + 1 ; j < in . length ( ) ; j + + )
{
TQChar d = in . at ( j ) ;
//kdDebug(14307) << "Got " << d << endl;
if ( d = = ' ( ' )
depth + + ;
if ( d = = ' ) ' )
{
// have we found the match?
if ( depth = = 0 )
{
// recursively replace contents of matching ()
TQString substitution = substDepthFirst ( player ,
in . mid ( i + 1 , j - i - 1 ) , true ) ;
in . tqreplace ( i , j - i + 1 , substitution ) ;
// perform substitution and return the result
i = i + substitution . length ( ) - 1 ;
break ;
}
else
depth - - ;
}
}
}
}
// no () found, perform substitution!
// get each string (to) to substitute for (from)
bool done = false ;
if ( in . tqcontains ( " %track " ) )
{
if ( track . isEmpty ( ) )
track = i18n ( " Unknown track " ) ;
in . tqreplace ( " %track " , track ) ;
done = true ;
}
if ( in . tqcontains ( " %artist " ) & & ! artist . isEmpty ( ) )
{
if ( artist . isEmpty ( ) )
artist = i18n ( " Unknown artist " ) ;
in . tqreplace ( " %artist " , artist ) ;
done = true ;
}
if ( in . tqcontains ( " %album " ) & & ! album . isEmpty ( ) )
{
if ( album . isEmpty ( ) )
album = i18n ( " Unknown album " ) ;
in . tqreplace ( " %album " , album ) ;
done = true ;
}
if ( in . tqcontains ( " %player " ) & & ! playerName . isEmpty ( ) )
{
if ( playerName . isEmpty ( ) )
playerName = i18n ( " Unknown player " ) ;
in . tqreplace ( " %player " , playerName ) ;
done = true ;
}
// make whether we return anything dependent on whether we
// were in brackets and if we were, if a substitution was made.
if ( inBrackets & & ! done )
return " " ;
return in ;
}
void NowListeningPlugin : : advertiseToChat ( Kopete : : ChatSession * theChat , TQString message )
{
Kopete : : ContactPtrList pl = theChat - > members ( ) ;
// get on with it
kdDebug ( 14307 ) < < k_funcinfo < <
( pl . isEmpty ( ) ? " has no " : " has " ) < < " interested recipients: " < < endl ;
/* for ( pl.first(); pl.current(); pl.next() )
kdDebug ( 14307 ) < < " NowListeningPlugin::advertiseNewTracks() " < < pl . current ( ) - > displayName ( ) < < endl ; */
// if no-one in this KMM wants to be advertised to, don't send
// any message
if ( pl . isEmpty ( ) )
return ;
Kopete : : Message msg ( theChat - > myself ( ) ,
pl ,
message ,
Kopete : : Message : : Outbound ,
Kopete : : Message : : RichText ) ;
theChat - > sendMessage ( msg ) ;
}
void NowListeningPlugin : : updateCurrentMediaPlayer ( )
{
kdDebug ( 14307 ) < < k_funcinfo < < " Update current media player (single mode) " < < endl ;
d - > m_currentMediaPlayer = d - > m_mediaPlayerList . at ( NowListeningConfig : : self ( ) - > selectedMediaPlayer ( ) ) ;
}
void NowListeningPlugin : : slotSettingsChanged ( )
{
// Force reading config
NowListeningConfig : : self ( ) - > readConfig ( ) ;
// Update the currentMediaPlayer, because config has changed.
if ( NowListeningConfig : : useSpecifiedMediaPlayer ( ) )
updateCurrentMediaPlayer ( ) ;
disconnect ( Kopete : : ChatSessionManager : : self ( ) ,
TQT_SIGNAL ( aboutToSend ( Kopete : : Message & ) ) ,
this ,
TQT_SLOT ( slotOutgoingMessage ( Kopete : : Message & ) ) ) ;
d - > advertTimer - > stop ( ) ;
disconnect ( d - > advertTimer , TQT_SIGNAL ( timeout ( ) ) , this , TQT_SLOT ( slotAdvertCurrentMusic ( ) ) ) ;
if ( NowListeningConfig : : self ( ) - > chatAdvertising ( ) )
{
kdDebug ( 14307 ) < < k_funcinfo < < " Now using chat window advertising. " < < endl ;
connect ( Kopete : : ChatSessionManager : : self ( ) ,
TQT_SIGNAL ( aboutToSend ( Kopete : : Message & ) ) ,
this ,
TQT_SLOT ( slotOutgoingMessage ( Kopete : : Message & ) ) ) ;
}
else if ( NowListeningConfig : : self ( ) - > statusAdvertising ( ) | | NowListeningConfig : : self ( ) - > appendStatusAdvertising ( ) )
{
kdDebug ( 14307 ) < < k_funcinfo < < " Now using status message advertising. " < < endl ;
connect ( d - > advertTimer , TQT_SIGNAL ( timeout ( ) ) , this , TQT_SLOT ( slotAdvertCurrentMusic ( ) ) ) ;
d - > advertTimer - > start ( 5000 ) ;
}
}
NowListeningPlugin * NowListeningPlugin : : pluginStatic_ = 0L ;
# include "nowlisteningplugin.moc"
// vim: set noet ts=4 sts=4 sw=4: