• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

interfaces

kimproxy.cpp

Go to the documentation of this file.
00001 /*    
00002     kimproxy.cpp
00003     
00004     IM service library for KDE
00005     
00006     Copyright (c) 2004 Will Stephenson   <lists@stevello.free-online.co.uk>
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include <qglobal.h>
00025 #include <qpixmapcache.h>
00026 #include <dcopclient.h>
00027 #include <kapplication.h>
00028 #include <kdcopservicestarter.h> 
00029 #include <kdebug.h>
00030 #include <kmessagebox.h>
00031 #include <ksimpleconfig.h>
00032 #include <kiconloader.h>
00033 #include <kservice.h>
00034 #include <kservicetype.h>
00035 
00036 #include "kimiface_stub.h"
00037 
00038 #include "kimproxy.h"
00039 
00040 static KStaticDeleter<KIMProxy> _staticDeleter;
00041 
00042 KIMProxy * KIMProxy::s_instance = 0L;
00043 
00044 struct AppPresenceCurrent
00045 {
00046     QCString appId;
00047     int presence;
00048 };
00049 
00050 class ContactPresenceListCurrent : public QValueList<AppPresenceCurrent>
00051 {
00052     public:
00053         // return value indicates if the supplied parameter was better than any existing presence
00054         bool update( const AppPresenceCurrent );
00055         AppPresenceCurrent best();
00056 };
00057 
00058 
00059 struct KIMProxy::Private
00060 {
00061     DCOPClient * dc;
00062     // list of the strings in use by KIMIface
00063     QStringList presence_strings;
00064     // list of the icon names in use by KIMIface
00065     QStringList presence_icons;
00066     // map of presences
00067     PresenceStringMap presence_map;
00068 };
00069 
00070 bool ContactPresenceListCurrent::update( AppPresenceCurrent ap )
00071 {
00072     if ( isEmpty() )
00073     {
00074         append( ap );
00075         return true;
00076     }
00077     
00078     bool bestChanged = false;
00079     AppPresenceCurrent best;
00080     best.presence = -1;
00081     ContactPresenceListCurrent::iterator it = begin();
00082     const ContactPresenceListCurrent::iterator itEnd = end();
00083     ContactPresenceListCurrent::iterator existing = itEnd;
00084 
00085     while ( it != itEnd )
00086     {
00087         if ( (*it).presence > best.presence )
00088             best = (*it);
00089         if ( (*it).appId == ap.appId )
00090             existing = it;
00091         ++it;
00092     }
00093     
00094     if ( ap.presence > best.presence ||
00095       best.appId == ap.appId )
00096         bestChanged = true;
00097     
00098     if ( existing != itEnd )
00099     {
00100         remove( existing );
00101         append( ap );
00102     }
00103     return bestChanged;
00104 }
00105 
00106 AppPresenceCurrent ContactPresenceListCurrent::best()
00107 {
00108     AppPresenceCurrent best;
00109     best.presence = -1;
00110     ContactPresenceListCurrent::iterator it = begin();
00111     const ContactPresenceListCurrent::iterator itEnd = end();
00112     while ( it != itEnd )
00113     {
00114         if ( (*it).presence > best.presence )
00115             best = (*it);
00116         ++it;
00117     }
00118     // if it's still -1 here, we have no presence data, so we return Unknown
00119     if ( best.presence == -1 )
00120         best.presence = 0;
00121     return best;
00122 }
00123 
00124 // int bestPresence( AppPresence* ap )
00125 // {
00126 //  Q_ASSERT( ap );
00127 //  AppPresence::const_iterator it;
00128 //  it = ap->begin();
00129 //  int best = 0; // unknown
00130 //  if ( it != ap->end() )
00131 //  {
00132 //      best = it.data();
00133 //      ++it;
00134 //      for ( ; it != ap->end(); ++it )
00135 //      {
00136 //          if ( it.data() > best )
00137 //              best = it.data(); 
00138 //      }
00139 //  }
00140 //  return best;
00141 // }
00142 // 
00143 // QCString bestAppId( AppPresence* ap )
00144 // {
00145 //  Q_ASSERT( ap );
00146 //  AppPresence::const_iterator it;
00147 //  QCString bestAppId;
00148 //  it = ap->begin();
00149 //  if ( it != ap->end() )
00150 //  {
00151 //      int best = it.data();
00152 //      bestAppId = it.key();
00153 //      ++it;
00154 //      for ( ; it != ap->end(); ++it )
00155 //      {
00156 //          if ( it.data() > best )
00157 //          {
00158 //              best = it.data();
00159 //              bestAppId = it.key();
00160 //          }
00161 //      }
00162 //  }
00163 //  return bestAppId;
00164 // }
00165 
00166 KIMProxy * KIMProxy::instance( DCOPClient * client ) 
00167 {
00168     if ( client )
00169     {
00170         if ( !s_instance )
00171             _staticDeleter.setObject( s_instance, new KIMProxy( client ) );
00172         return s_instance;
00173     }
00174     else
00175         return 0L;
00176 }
00177 
00178 KIMProxy::KIMProxy( DCOPClient* dc ) : DCOPObject( "KIMProxyIface" ), QObject(), d( new Private )
00179 {
00180     m_im_client_stubs.setAutoDelete( true );
00181 
00182     d->dc = dc;
00183     m_initialized = false;
00184     connect( d->dc, SIGNAL( applicationRemoved( const QCString& ) ) , this, SLOT( unregisteredFromDCOP( const QCString& ) ) );
00185     connect( d->dc, SIGNAL( applicationRegistered( const QCString& ) ) , this, SLOT( registeredToDCOP( const QCString& ) ) );
00186     d->dc->setNotifications( true );
00187 
00188     d->presence_strings.append( "Unknown" );
00189     d->presence_strings.append( "Offline" );
00190     d->presence_strings.append( "Connecting" );
00191     d->presence_strings.append( "Away" );
00192     d->presence_strings.append( "Online" );
00193     
00194     d->presence_icons.append( "presence_unknown" );
00195     d->presence_icons.append( "presence_offline" );
00196     d->presence_icons.append( "presence_connecting" );
00197     d->presence_icons.append( "presence_away" );
00198     d->presence_icons.append( "presence_online" );
00199     
00200     //QCString senderApp = "Kopete";
00201     //QCString senderObjectId = "KIMIface";
00202     QCString method = "contactPresenceChanged( QString, QCString, int )";
00203     //QCString receiverObjectId = "KIMProxyIface";
00204     
00205     // FIXME: make this work when the sender object id is set to KIMIFace
00206     if ( !connectDCOPSignal( 0, 0, method, method, false ) )
00207         KMessageBox::information( 0, QString( "Couldn't connect DCOP signal.\nWon't receive any status notifications!" ) );
00208 }
00209 
00210 KIMProxy::~KIMProxy( )
00211 {
00212     //d->dc->setNotifications( false );
00213 }
00214 
00215 bool KIMProxy::initialize()
00216 {
00217     if ( !m_initialized )
00218     {
00219         m_initialized = true; // we should only do this once, as registeredToDCOP() will catch any new starts
00220         // So there is no error from a failed query when using kdelibs 3.2, which don't have this servicetype
00221         if ( KServiceType::serviceType( IM_SERVICE_TYPE ) ) 
00222         {
00223             //kdDebug( 790 ) << k_funcinfo << endl;
00224             QCString dcopObjectId = "KIMIface";
00225     
00226             // see what apps implementing our service type are out there
00227             KService::List offers = KServiceType::offers( IM_SERVICE_TYPE );
00228             KService::List::iterator offer;
00229             typedef QValueList<QCString> QCStringList;
00230             QCStringList registeredApps = d->dc->registeredApplications();
00231             QCStringList::iterator app;
00232             const QCStringList::iterator end = registeredApps.end();
00233             // for each registered app
00234             for ( app = registeredApps.begin(); app != end; ++app )
00235             {
00236                 //kdDebug( 790 ) << " considering: " << *app << endl;
00237                 //for each offer
00238                 for ( offer = offers.begin(); offer != offers.end(); ++offer )
00239                 {
00240                     QCString dcopService = (*offer)->property("X-DCOP-ServiceName").toString().latin1();
00241                     if ( !dcopService.isEmpty() )
00242                     {
00243                         //kdDebug( 790 ) << " is it: " << dcopService << "?" << endl;
00244                         // get the application name ( minus any process ID )
00245                         QCString instanceName =  (*app).left( dcopService.length() );
00246                         // if the application implements the dcop service, add it 
00247                         if ( instanceName == dcopService )
00248                         {
00249                             m_apps_available = true;
00250                             //kdDebug( 790 ) << " app name: " << (*offer)->name() << ", has instance " << *app << ", dcopService: " << dcopService << endl;
00251                             if ( !m_im_client_stubs.find( dcopService ) )
00252                             {
00253                                 kdDebug( 790 ) << "App " << *app << ", dcopObjectId " << dcopObjectId << " found, using it for presence info." << endl;
00254                                 m_im_client_stubs.insert( *app, new KIMIface_stub( d->dc, *app, dcopObjectId ) );
00255                                 pollApp( *app );
00256                             }
00257                         }
00258                     }
00259                 }
00260             }
00261         }
00262     }
00263     return !m_im_client_stubs.isEmpty();
00264 }
00265 
00266 void KIMProxy::registeredToDCOP( const QCString& appId )
00267 {
00268     //kdDebug( 790 ) << k_funcinfo << " appId '" << appId << "'" << endl;
00269     // check that appId implements our service
00270     // if the appId ends with a number, i.e. a pid like in foobar-12345,
00271     if ( appId.isEmpty() )
00272         return;
00273 
00274     bool newApp = false;
00275     // get an up to date list of offers in case a new app was installed
00276     // and check each of the offers that implement the service type we're looking for,
00277     // to see if any of them are the app that just registered
00278     const KService::List offers = KServiceType::offers( IM_SERVICE_TYPE );
00279     KService::List::const_iterator it;
00280     for ( it = offers.begin(); it != offers.end(); ++it )
00281     {
00282         QCString dcopObjectId = "KIMIface";
00283         QCString dcopService = (*it)->property("X-DCOP-ServiceName").toString().latin1();
00284         if ( appId.left( dcopService.length() ) == dcopService )
00285         {
00286             // if it's not already known, insert it
00287             if ( !m_im_client_stubs.find( appId ) )
00288             {
00289                 newApp = true;
00290                 kdDebug( 790 ) << "App: " << appId << ", dcopService: " << dcopService << " started, using it for presence info."<< endl;
00291                 m_im_client_stubs.insert( appId, new KIMIface_stub( d->dc, appId, dcopObjectId ) );
00292             }
00293         }
00294         //else
00295         //  kdDebug( 790 ) << "App doesn't implement our ServiceType" << endl;
00296     }
00297     //if ( newApp )
00298     //  emit sigPresenceInfoExpired();
00299 }
00300 
00301 void KIMProxy::unregisteredFromDCOP( const QCString& appId )
00302 {
00303     //kdDebug( 790 ) << k_funcinfo << appId << endl;
00304     if ( m_im_client_stubs.find( appId ) )
00305     {
00306         kdDebug( 790 ) << appId << " quit, removing its presence info." << endl;
00307         
00308         PresenceStringMap::Iterator it = d->presence_map.begin();
00309         const PresenceStringMap::Iterator end = d->presence_map.end();
00310         for ( ; it != end; ++it )
00311         {
00312             ContactPresenceListCurrent list = it.data();
00313             ContactPresenceListCurrent::iterator cpIt = list.begin();
00314             while( cpIt != list.end() )
00315             {
00316                 ContactPresenceListCurrent::iterator gone = cpIt++;
00317                 if ( (*gone).appId == appId )
00318                 {
00319                     list.remove( gone );
00320                 }
00321             }
00322         }
00323         m_im_client_stubs.remove( appId );
00324         emit sigPresenceInfoExpired();
00325     }
00326 }
00327 
00328 void KIMProxy::contactPresenceChanged( QString uid, QCString appId, int presence )
00329 {
00330     // update the presence map
00331     //kdDebug( 790 ) << k_funcinfo << "uid: " << uid << " appId: " << appId << " presence " << presence << endl;
00332     ContactPresenceListCurrent current;
00333     current = d->presence_map[ uid ];
00334   //kdDebug( 790 ) << "current best presence from : " << current.best().appId << " is: " << current.best().presence << endl;
00335     AppPresenceCurrent newPresence;
00336     newPresence.appId = appId;
00337     newPresence.presence = presence;
00338 
00339     if ( current.update( newPresence ) )
00340     {
00341         d->presence_map.insert( uid, current );
00342         emit sigContactPresenceChanged( uid );
00343     }
00344 }
00345 
00346 int KIMProxy::presenceNumeric( const QString& uid )
00347 {
00348     AppPresenceCurrent ap;
00349     ap.presence = 0;
00350     if ( initialize() )
00351     {
00352         ContactPresenceListCurrent presence = d->presence_map[ uid ];
00353         ap = presence.best();
00354     }
00355     return ap.presence;
00356 }
00357 
00358 QString KIMProxy::presenceString( const QString& uid )
00359 {
00360     AppPresenceCurrent ap;
00361     ap.presence = 0;
00362     if ( initialize() )
00363     {
00364         ContactPresenceListCurrent presence = d->presence_map[ uid ];
00365         ap = presence.best();
00366     }
00367     if ( ap.appId.isEmpty() )
00368         return QString::null;
00369     else
00370         return d->presence_strings[ ap.presence ];
00371 }
00372 
00373 QPixmap KIMProxy::presenceIcon( const QString& uid )
00374 {
00375     AppPresenceCurrent ap;
00376     ap.presence = 0;
00377     if ( initialize() )
00378     {
00379         ContactPresenceListCurrent presence = d->presence_map[ uid ];
00380         ap = presence.best();
00381     }
00382     if ( ap.appId.isEmpty() )
00383     {
00384         //kdDebug( 790 ) << k_funcinfo << "returning a null QPixmap because we were asked for an icon for a uid we know nothing about" << endl;
00385         return QPixmap();
00386     }
00387     else
00388     {
00389         //kdDebug( 790 ) << k_funcinfo << "returning this: " << d->presence_icons[ ap.presence ] << endl;
00390         return SmallIcon( d->presence_icons[ ap.presence ]);
00391     }
00392 }
00393 
00394 QStringList KIMProxy::allContacts()
00395 {
00396     QStringList value = d->presence_map.keys();
00397     return value;
00398 }
00399 
00400 QStringList KIMProxy::reachableContacts()
00401 {
00402     QStringList value;
00403     
00404     if ( initialize() )
00405     {
00406         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00407         for ( ; it.current(); ++it )
00408         {
00409             value += it.current()->reachableContacts( );
00410         }
00411     }
00412     return value;
00413 }
00414 
00415 QStringList KIMProxy::onlineContacts()
00416 {
00417     QStringList value;
00418     PresenceStringMap::iterator it = d->presence_map.begin();
00419     const PresenceStringMap::iterator end= d->presence_map.end();
00420     for ( ; it != end; ++it )
00421         if ( it.data().best().presence > 2 /*Better than Connecting, ie Away or Online*/ )
00422             value.append( it.key() );
00423         
00424     return value;
00425 }
00426 
00427 QStringList KIMProxy::fileTransferContacts()
00428 {
00429     QStringList value;
00430     
00431     if ( initialize() )
00432     {
00433         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00434         for ( ; it.current(); ++it )
00435         {
00436             value += it.current()->fileTransferContacts( );
00437         }
00438     }
00439     return value;
00440 }
00441 
00442 bool KIMProxy::isPresent( const QString& uid )
00443 {
00444     return ( !d->presence_map[ uid ].isEmpty() );
00445 }
00446 
00447 QString KIMProxy::displayName( const QString& uid )
00448 {
00449     QString name;
00450     if ( initialize() )
00451     {
00452         if ( KIMIface_stub* s = stubForUid( uid ) )
00453             name = s->displayName( uid );
00454     }
00455     //kdDebug( 790 ) << k_funcinfo << name << endl;
00456     return name;
00457 }
00458 
00459 bool KIMProxy::canReceiveFiles( const QString & uid )
00460 {
00461     if ( initialize() )
00462     {
00463         if ( KIMIface_stub* s = stubForUid( uid ) )
00464             return s->canReceiveFiles( uid );
00465     }
00466     return false;
00467 }
00468 
00469 bool KIMProxy::canRespond( const QString & uid )
00470 {
00471     if ( initialize() )
00472     {
00473         if ( KIMIface_stub* s = stubForUid( uid ) )
00474             return s->canRespond( uid );
00475     }
00476     return false;
00477 }
00478 
00479 QString KIMProxy::context( const QString & uid )
00480 {
00481     if ( initialize() )
00482     {
00483         if ( KIMIface_stub* s = stubForUid( uid ) )
00484             return s->context( uid );
00485     }
00486     return QString::null;
00487 }
00488     
00489 void KIMProxy::chatWithContact( const QString& uid )
00490 {
00491     if ( initialize() )
00492     {
00493         if ( KIMIface_stub* s = stubForUid( uid ) )
00494         {
00495             kapp->updateRemoteUserTimestamp( s->app() );
00496             s->chatWithContact( uid );
00497         }
00498     }
00499     return;
00500 }
00501 
00502 void KIMProxy::messageContact( const QString& uid, const QString& message )
00503 {
00504     if ( initialize() )
00505     {
00506         if ( KIMIface_stub* s = stubForUid( uid ) )
00507         {
00508             kapp->updateRemoteUserTimestamp( s->app() );
00509             s->messageContact( uid, message );
00510         }
00511     }
00512     return;
00513 }
00514 
00515 void KIMProxy::sendFile(const QString &uid, const KURL &sourceURL, const QString &altFileName, uint fileSize )
00516 {
00517     if ( initialize() )
00518     {
00519         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00520         for ( ; it.current(); ++it )
00521         {
00522             if ( it.current()->canReceiveFiles( uid ) )
00523             {
00524                 kapp->updateRemoteUserTimestamp( it.current()->app() );
00525                 it.current()->sendFile( uid, sourceURL, altFileName, fileSize );
00526                 break;
00527             }
00528         }   
00529     }
00530     return;
00531 }
00532 
00533 bool KIMProxy::addContact( const QString &contactId, const QString &protocol )
00534 {
00535     if ( initialize() )
00536     {
00537         if ( KIMIface_stub* s = stubForProtocol( protocol ) )
00538         return s->addContact( contactId, protocol );
00539     }
00540     return false;
00541 }
00542 
00543 QString KIMProxy::locate( const QString & contactId, const QString & protocol )
00544 {
00545     if ( initialize() )
00546     {
00547         if ( KIMIface_stub* s = stubForProtocol( protocol ) )
00548         return s->locate( contactId, protocol );
00549     }
00550     return QString::null;
00551 }
00552 
00553 bool KIMProxy::imAppsAvailable()
00554 {
00555     return ( !m_im_client_stubs.isEmpty() );
00556 }
00557 
00558 bool KIMProxy::startPreferredApp()
00559 {
00560     QString preferences = QString("[X-DCOP-ServiceName] = '%1'").arg( preferredApp() );
00561     // start/find an instance of DCOP/InstantMessenger
00562     QString error;
00563     QCString dcopService;
00564     // Get a preferred IM client.
00565     // The app will notify itself to us using registeredToDCOP, so we don't need to record a stub for it here
00566     // FIXME: error in preferences, see debug output
00567     preferences = QString::null;
00568     int result = KDCOPServiceStarter::self()->findServiceFor( IM_SERVICE_TYPE, QString::null, preferences, &error, &dcopService );
00569 
00570     kdDebug( 790 ) << k_funcinfo << "error was: " << error << ", dcopService: " << dcopService << endl;
00571 
00572     return ( result == 0 );
00573 }
00574 
00575 
00576 void KIMProxy::pollAll( const QString &uid )
00577 {
00578 /*  // We only need to call this function if we don't have any data at all
00579     // otherwise, the data will be kept fresh by received presence change
00580     // DCOP signals
00581     if ( !d->presence_map.contains( uid ) )
00582     {
00583         AppPresence *presence = new AppPresence();
00584         // record current presence from known clients
00585         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00586         for ( ; it.current(); ++it )
00587         {
00588             presence->insert( it.currentKey().ascii(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys...
00589         }
00590         d->presence_map.insert( uid, presence );
00591     }*/
00592 }
00593 
00594 void KIMProxy::pollApp( const QCString & appId )
00595 {
00596     //kdDebug( 790 ) << k_funcinfo << endl;
00597     KIMIface_stub * appStub = m_im_client_stubs[ appId ];
00598     QStringList contacts = m_im_client_stubs[ appId ]->allContacts();
00599     QStringList::iterator it = contacts.begin();
00600     QStringList::iterator end = contacts.end();
00601     for ( ; it != end; ++it )
00602     {
00603         ContactPresenceListCurrent current = d->presence_map[ *it ];
00604         AppPresenceCurrent ap;
00605         ap.appId = appId;
00606         ap.presence = appStub->presenceStatus( *it );
00607         current.append( ap );
00608 
00609         d->presence_map.insert( *it, current );
00610         if ( current.update( ap ) )
00611             emit sigContactPresenceChanged( *it );
00612         //kdDebug( 790 ) << " uid: " << *it << " presence: " << ap.presence << endl;
00613     }
00614 }
00615 
00616 KIMIface_stub * KIMProxy::stubForUid( const QString &uid )
00617 {
00618     // get best appPresence
00619     AppPresenceCurrent ap = d->presence_map[ uid ].best();
00620     // look up the presence string from that app
00621         return m_im_client_stubs.find( ap.appId );
00622 }
00623 
00624 KIMIface_stub * KIMProxy::stubForProtocol( const QString &protocol)
00625 {
00626     KIMIface_stub * app;
00627     // see if the preferred client supports this protocol
00628     QString preferred = preferredApp();
00629     if ( ( app = m_im_client_stubs.find( preferred ) ) )
00630     {
00631         if ( app->protocols().grep( protocol ).count() > 0 )
00632             return app;
00633     }
00634     // preferred doesn't do this protocol, try the first of the others that says it does
00635     QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00636     for ( ; it.current(); ++it )
00637     {
00638         if ( it.current()->protocols().grep( protocol ).count() > 0 )
00639             return it.current();
00640     }   
00641     return 0L;
00642 }
00643 
00644 QString KIMProxy::preferredApp()
00645 {
00646     KConfig *store = new KSimpleConfig( IM_CLIENT_PREFERENCES_FILE );
00647     store->setGroup( IM_CLIENT_PREFERENCES_SECTION );
00648     QString preferredApp = store->readEntry( IM_CLIENT_PREFERENCES_ENTRY );
00649     //kdDebug( 790 ) << k_funcinfo << "found preferred app: " << preferredApp << endl;
00650     return preferredApp;
00651 }   
00652 
00653 #include "kimproxy.moc"

interfaces

Skip menu "interfaces"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal