/* systemtray.cpp - Kopete Tray Dock Icon Copyright (c) 2002 by Nick Betcher Copyright (c) 2002-2003 by Martijn Klingens Copyright (c) 2003-2004 by Olivier Goffart Kopete (c) 2002-2005 by the Kopete developers ************************************************************************* * * * 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 "systemtray.h" #include #include #include #include #include #include #include #include #include #include "kopeteuiglobal.h" #include "kopetechatsessionmanager.h" #include "kopeteballoon.h" #include "kopeteprefs.h" #include "kopetemetacontact.h" #include "kopeteaccount.h" #include "kopeteaccountmanager.h" #include "kopetecontact.h" #include "kopetewindow.h" KopeteSystemTray* KopeteSystemTray::s_systemTray = 0L; KopeteSystemTray* KopeteSystemTray::systemTray( TQWidget *parent, const char* name ) { if( !s_systemTray ) s_systemTray = new KopeteSystemTray( parent, name ); return s_systemTray; } KopeteSystemTray::KopeteSystemTray(TQWidget* parent, const char* name) : KSystemTray(parent,name) { // kdDebug(14010) << "Creating KopeteSystemTray" << endl; TQToolTip::add( this, kapp->aboutData()->shortDescription() ); mIsBlinkIcon = false; mIsBlinking = false; mBlinkTimer = new TQTimer(this, "mBlinkTimer"); mKopeteIcon = loadIcon("kopete"); connect(mBlinkTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotBlink())); connect(Kopete::ChatSessionManager::self() , TQT_SIGNAL(newEvent(Kopete::MessageEvent*)), this, TQT_SLOT(slotNewEvent(Kopete::MessageEvent*))); connect(KopetePrefs::prefs(), TQT_SIGNAL(saved()), this, TQT_SLOT(slotConfigChanged())); connect(Kopete::AccountManager::self(), TQT_SIGNAL(accountOnlineStatusChanged(Kopete::Account *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus &)), this, TQT_SLOT(slotReevaluateAccountStates())); // the slot called by default by the quit action, KSystemTray::maybeQuit(), // just closes the parent window, which is hard to distinguish in that window's closeEvent() // from a click on the window's close widget // in the quit case, we want to quit the application // in the close widget click case, we only want to hide the parent window // so instead, we make it call our general purpose quit slot on the window, which causes a window close and everything else we need // KDE4 - app will have to listen for quitSelected instead TDEAction *quit = actionCollection()->action( "file_quit" ); quit->disconnect(); KopeteWindow *myParent = static_cast( parent ); connect( quit, TQT_SIGNAL( activated() ), myParent, TQT_SLOT( slotQuit() ) ); //setPixmap(mKopeteIcon); slotReevaluateAccountStates(); slotConfigChanged(); m_balloon=0l; } KopeteSystemTray::~KopeteSystemTray() { // kdDebug(14010) << "[KopeteSystemTray] ~KopeteSystemTray" << endl; // delete mBlinkTimer; Kopete::UI::Global::setSysTrayWId( 0 ); } void KopeteSystemTray::mousePressEvent( TQMouseEvent *me ) { if ( (me->button() == Qt::MidButton || (me->button() == Qt::LeftButton && KopetePrefs::prefs()->trayflashNotifyLeftClickOpensMessage())) && mIsBlinking ) { mouseDoubleClickEvent( me ); return; } KSystemTray::mousePressEvent( me ); } void KopeteSystemTray::mouseDoubleClickEvent( TQMouseEvent *me ) { if ( !mIsBlinking ) { KSystemTray::mousePressEvent( me ); } else { if(!mEventList.isEmpty()) mEventList.first()->apply(); } } void KopeteSystemTray::contextMenuAboutToShow( TDEPopupMenu *me ) { //kdDebug(14010) << k_funcinfo << "Called." << endl; emit aboutToShowMenu( me ); } void KopeteSystemTray::startBlink( const TQString &icon ) { startBlink( TDEGlobal::iconLoader()->loadIcon( icon , TDEIcon::Panel ) ); } void KopeteSystemTray::startBlink( const TQPixmap &icon ) { mBlinkIcon = icon; if ( mBlinkTimer->isActive() == false ) { mIsBlinkIcon = true; mIsBlinking = true; mBlinkTimer->start( 1000, false ); } else { mBlinkTimer->stop(); mIsBlinkIcon = true; mIsBlinking = true; mBlinkTimer->start( 1000, false ); } } void KopeteSystemTray::startBlink( const TQMovie &movie ) { //kdDebug( 14010 ) << k_funcinfo << "starting movie." << endl; const_cast( movie ).unpause(); setMovie( movie ); mIsBlinking = true; } void KopeteSystemTray::startBlink() { if ( mMovie.isNull() ) mMovie = TDEGlobal::iconLoader()->loadMovie( TQString::fromLatin1( "newmessage" ), TDEIcon::Panel ); startBlink( mMovie ); } void KopeteSystemTray::stopBlink() { if ( movie() ) kdDebug( 14010 ) << k_funcinfo << "stopping movie." << endl; else if ( mBlinkTimer->isActive() ) mBlinkTimer->stop(); if ( !mMovie.isNull() ) mMovie.pause(); mIsBlinkIcon = false; mIsBlinking = false; //setPixmap( mKopeteIcon ); slotReevaluateAccountStates(); } void KopeteSystemTray::slotBlink() { setPixmap( mIsBlinkIcon ? mKopeteIcon : mBlinkIcon ); mIsBlinkIcon = !mIsBlinkIcon; } void KopeteSystemTray::slotNewEvent( Kopete::MessageEvent *event ) { if( KopetePrefs::prefs()->useStack() ) { mEventList.prepend( event ); mBalloonEventList.prepend( event ); } else { mEventList.append( event ); mBalloonEventList.append( event ); } connect(event, TQT_SIGNAL(done(Kopete::MessageEvent*)), this, TQT_SLOT(slotEventDone(Kopete::MessageEvent*))); if( event->message().manager() != 0 ) { if( event->message().manager()->account() ) { if( !event->message().manager()->account()->isAway() || KopetePrefs::prefs()->soundIfAway() ) { addBalloon(); } else { kdDebug(14000) << k_funcinfo << "Supressing balloon, account is away" << endl; } } } else kdDebug(14000) << k_funcinfo << "NULL message().manager()!" << endl; // tray animation if ( KopetePrefs::prefs()->trayflashNotify() ) if( mBalloonEventList.count() == mEventList.count() ) startBlink(); else stopBlink(); } void KopeteSystemTray::slotEventDone(Kopete::MessageEvent *event) { mEventList.remove(event); removeBalloonEvent(event); if(mEventList.isEmpty()) stopBlink(); } void KopeteSystemTray::slotRemoveBalloon() { removeBalloonEvent(mBalloonEventList.first()); } void KopeteSystemTray::removeBalloonEvent(Kopete::MessageEvent *event) { bool current= event==mBalloonEventList.first(); mBalloonEventList.remove(event); if(current && m_balloon) { m_balloon->deleteLater(); m_balloon=0l; if(!mBalloonEventList.isEmpty()) { //delay the addBalloon to let the time to event be deleted //in case a contact has been deleted cf Bug 100196 TQTimer::singleShot(0, this, TQT_SLOT(addBalloon())); } else { if(KopetePrefs::prefs()->trayflashNotify() && !mEventList.isEmpty()) startBlink(); } } } void KopeteSystemTray::addBalloon() { /*kdDebug(14010) << k_funcinfo << m_balloon << ":" << KopetePrefs::prefs()->showTray() << ":" << KopetePrefs::prefs()->balloonNotify() << ":" << !mBalloonEventList.isEmpty() << endl;*/ if( m_balloon && KopetePrefs::prefs()->useStack() ) { m_balloon->deleteLater(); m_balloon=0l; } if( !m_balloon && KopetePrefs::prefs()->showTray() && KopetePrefs::prefs()->balloonNotify() && !mBalloonEventList.isEmpty() ) { Kopete::Message msg = mBalloonEventList.first()->message(); if ( msg.from() ) { TQString msgText = squashMessage( msg ); kdDebug(14010) << k_funcinfo << "msg text=" << msgText << endl; TQString msgFrom; if( msg.from()->metaContact() ) msgFrom = msg.from()->metaContact()->displayName(); else msgFrom = msg.from()->contactId(); m_balloon = new KopeteBalloon( i18n( "New Message from %1:
\"%2\"
" ) .arg( TQStyleSheet::escape( msgFrom ), msgText ), TQString() ); connect(m_balloon, TQT_SIGNAL(signalBalloonClicked()), mBalloonEventList.first() , TQT_SLOT(apply())); connect(m_balloon, TQT_SIGNAL(signalButtonClicked()), mBalloonEventList.first() , TQT_SLOT(apply())); connect(m_balloon, TQT_SIGNAL(signalIgnoreButtonClicked()), mBalloonEventList.first() , TQT_SLOT(ignore())); connect(m_balloon, TQT_SIGNAL(signalTimeout()), this , TQT_SLOT(slotRemoveBalloon())); m_balloon->setAnchor(mapToGlobal(pos())); m_balloon->show(); KWin::setOnAllDesktops(m_balloon->winId(), true); } } } void KopeteSystemTray::slotConfigChanged() { // kdDebug(14010) << k_funcinfo << "called." << endl; if ( KopetePrefs::prefs()->showTray() ) show(); else hide(); // for users without kicker or a similar docking app } void KopeteSystemTray::slotReevaluateAccountStates() { // If there is a pending message, we don't need to refresh the system tray now. // This function will even be called when the animation will stop. if ( mIsBlinking ) return; //kdDebug(14010) << k_funcinfo << endl; bool bOnline = false; bool bAway = false; bool bOffline = false; Kopete::Contact *c = 0; for (TQPtrListIterator it(Kopete::AccountManager::self()->accounts()); it.current(); ++it) { c = it.current()->myself(); if (!c) continue; if (c->onlineStatus().status() == Kopete::OnlineStatus::Online) { bOnline = true; // at least one contact is online } else if (c->onlineStatus().status() == Kopete::OnlineStatus::Away || c->onlineStatus().status() == Kopete::OnlineStatus::Invisible) { bAway = true; // at least one contact is away or invisible } else // this account must be offline (or unknown, which I don't know how to handle) { bOffline = true; } } if (!bOnline && !bAway && !bOffline) // special case, no accounts defined (yet) bOffline = true; if (bAway) { if (!bOnline && !bOffline) // none online and none offline -> all away setPixmap(loadIcon("kopete_all_away")); else setPixmap(loadIcon("kopete_some_away")); } else if(bOnline) { /*if(bOffline) // at least one offline and at least one online -> some accounts online setPixmap(loadIcon("kopete_some_online")); else*/ // none offline and none away -> all online setPixmap(mKopeteIcon); } else // none away and none online -> all offline { //kdDebug(14010) << k_funcinfo << "All Accounts offline!" << endl; setPixmap(loadIcon("kopete_offline")); } } TQString KopeteSystemTray::squashMessage( const Kopete::Message& msg ) { TQString msgText = msg.parsedBody(); TQRegExp rx( "(((http://)?(.+)))" ); rx.setMinimal( true ); if ( rx.search( msgText ) == -1 ) { // no URLs in text, just pick the first 30 chars of // the parsed text if necessary. We used parsed text // so that things like "" show correctly // Escape it after snipping it to not snip entities msgText =msg.plainBody() ; if( msgText.length() > 30 ) msgText = msgText.left( 30 ) + TQString::fromLatin1( " ..." ); msgText=Kopete::Message::escape(msgText); } else { TQString plainText = msg.plainBody(); if ( plainText.length() > 30 ) { TQString fullUrl = rx.cap( 2 ); TQString shorterUrl; if ( fullUrl.length() > 30 ) { TQString urlWithoutProtocol = rx.cap( 4 ); shorterUrl = urlWithoutProtocol.left( 27 ) + TQString::fromLatin1( "... " ); } else { shorterUrl = fullUrl.left( 27 ) + TQString::fromLatin1( "... " ); } // remove message text msgText = TQString::fromLatin1( "... " ) + rx.cap( 1 ) + TQString::fromLatin1( " ..." ); // find last occurrence of URL (the one inside the tag) int revUrlOffset = msgText.findRev( fullUrl ); msgText.replace( revUrlOffset, fullUrl.length(), shorterUrl ); } } return msgText; } #include "systemtray.moc" // vim: set noet ts=4 sts=4 sw=4: