knotify.cpp

00001 /*
00002    Copyright (c) 1997 Christian Esken (esken@kde.org)
00003                  2000 Charles Samuels (charles@kde.org)
00004                  2000 Stefan Schimanski (1Stein@gmx.de)
00005                  2000 Matthias Ettrich (ettrich@kde.org)
00006                  2000 Waldo Bastian <bastian@kde.org>
00007                  2000-2003 Carsten Pfeiffer <pfeiffer@kde.org>
00008 
00009    This program is free software; you can redistribute it and/or modify
00010    it under the terms of the GNU General Public License as published by
00011    the Free Software Foundation; either version 2, or (at your option)
00012    any later version.
00013 
00014    This program is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017    GNU General Public License for more details.
00018 
00019    You should have received a copy of the GNU General Public License
00020    along with this program; if not, write to the Free Software
00021    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00022 */
00023 
00024 // C headers
00025 #include <fcntl.h>
00026 #include <sys/types.h>
00027 #include <sys/stat.h>
00028 
00029 #include <config.h>
00030 #ifndef WITHOUT_ARTS
00031 // aRts headers
00032 #include <connect.h>
00033 #include <dispatcher.h>
00034 #include <flowsystem.h>
00035 #include <qiomanager.h>
00036 #include <soundserver.h>
00037 #endif
00038 
00039 // QT headers
00040 #include <qfile.h>
00041 #include <qfileinfo.h>
00042 #include <qstringlist.h>
00043 #include <qtextstream.h>
00044 
00045 // KDE headers
00046 #include <dcopclient.h>
00047 #include <kaboutdata.h>
00048 #ifndef WITHOUT_ARTS
00049 #include <kartsdispatcher.h>
00050 #include <kartsserver.h>
00051 #endif
00052 #include <kcmdlineargs.h>
00053 #include <kconfig.h>
00054 #include <kdebug.h>
00055 #include <kglobal.h>
00056 #include <klocale.h>
00057 #include <kmessagebox.h>
00058 #include <kpassivepopup.h>
00059 #include <kiconloader.h>
00060 #include <kmacroexpander.h>
00061 #ifndef WITHOUT_ARTS
00062 #include <kplayobjectfactory.h>
00063 #include <kaudiomanagerplay.h>
00064 #endif
00065 #include <kprocess.h>
00066 #include <kstandarddirs.h>
00067 #include <kuniqueapplication.h>
00068 #include <kwin.h>
00069 
00070 #include "knotify.h"
00071 #include "knotify.moc"
00072 
00073 class KNotifyPrivate
00074 {
00075 public:
00076     KConfig* globalEvents;
00077     KConfig* globalConfig;
00078     QMap<QString, KConfig*> events;
00079     QMap<QString, KConfig*> configs;
00080     QString externalPlayer;
00081     KProcess *externalPlayerProc;
00082 
00083 #ifndef WITHOUT_ARTS
00084     QPtrList<KDE::PlayObject> playObjects;
00085     QMap<KDE::PlayObject*,int> playObjectEventMap;
00086     KAudioManagerPlay *audioManager;
00087 #endif
00088     int externalPlayerEventId;
00089 
00090     bool useExternal;
00091     bool useArts;
00092     int volume;
00093     QTimer *playTimer;
00094     bool inStartup;
00095     QString startupEvents;
00096 };
00097 
00098 // Yes, it's ugly to put this here, but this facilitates the cautious startup
00099 // procedure.
00100 #ifndef WITHOUT_ARTS
00101 KArtsServer *soundServer = 0;
00102 #endif
00103 
00104 extern "C"{
00105 
00106 KDE_EXPORT int kdemain(int argc, char **argv)
00107 {
00108     KAboutData aboutdata("knotify", I18N_NOOP("KNotify"),
00109                          "3.0", I18N_NOOP("KDE Notification Server"),
00110                          KAboutData::License_GPL, "(C) 1997-2003, KDE Developers");
00111     aboutdata.addAuthor("Carsten Pfeiffer",I18N_NOOP("Current Maintainer"),"pfeiffer@kde.org");
00112     aboutdata.addAuthor("Christian Esken",0,"esken@kde.org");
00113     aboutdata.addAuthor("Stefan Westerfeld",I18N_NOOP("Sound support"),"stefan@space.twc.de");
00114     aboutdata.addAuthor("Charles Samuels",I18N_NOOP("Previous Maintainer"),"charles@kde.org");
00115 
00116     KCmdLineArgs::init( argc, argv, &aboutdata );
00117     KUniqueApplication::addCmdLineOptions();
00118 
00119 
00120     // initialize application
00121     if ( !KUniqueApplication::start() ) {
00122         kdDebug() << "Running knotify found" << endl;
00123         return 0;
00124     }
00125 
00126     KUniqueApplication app;
00127     app.disableSessionManagement();
00128 
00129     // KNotify is started on KDE startup and on demand (using
00130     // KNotifClient::startDaemon()) whenever a KNotify event occurs. Especially
00131     // KWin may fire many events (e.g. when a window pops up). When we have
00132     // problems with aRts or the installation, we might get an infinite loop
00133     // of knotify crashing, popping up the crashhandler window and kwin firing
00134     // another event, starting knotify again...
00135     // We try to prevent this by tracking our startup and offer options to
00136     // abort this.
00137 
00138 #ifndef WITHOUT_ARTS
00139     KConfigGroup config( KGlobal::config(), "StartProgress" );
00140     KConfig artsKCMConfig( "kcmartsrc" );
00141     artsKCMConfig.setGroup( "Arts" );
00142     bool useArts = artsKCMConfig.readBoolEntry( "StartServer", true );
00143     if (useArts)
00144         useArts = config.readBoolEntry( "Use Arts", useArts );
00145     bool ok = config.readBoolEntry( "Arts Init", true );
00146 
00147     if ( useArts && !ok )
00148     {
00149         if ( KMessageBox::questionYesNo(
00150                  0L,
00151                  i18n("During the previous startup, KNotify crashed while creating "
00152                       "Arts::Dispatcher. Do you want to try again or disable "
00153                       "aRts sound output?\n\n"
00154                       "If you choose to disable aRts output now, you can re-enable "
00155                       "it later or select an alternate sound player "
00156                       "in the System Notifications control panel."),
00157                  i18n("KNotify Problem"),
00158                  i18n("&Try Again"),
00159                  i18n("D&isable aRts Output"),
00160                  "KNotifyStartProgress",
00161                  0 /* don't call KNotify :) */
00162                  )
00163              == KMessageBox::No )
00164         {
00165             useArts = false;
00166         }
00167     }
00168 
00169     // when ArtsDispatcher crashes, we know it the next start.
00170     config.writeEntry( "Arts Init", false );
00171     config.writeEntry( "Use Arts", useArts );
00172     config.sync();
00173 
00174     KArtsDispatcher *dispatcher = 0;
00175     if ( useArts )
00176     {
00177         dispatcher = new KArtsDispatcher;
00178         soundServer = new KArtsServer;
00179     }
00180 
00181     // ok, seemed to work.
00182     config.writeEntry("Arts Init", useArts );
00183     config.sync();
00184 
00185     ok = config.readBoolEntry( "KNotify Init", true );
00186     if ( useArts && !ok )
00187     {
00188         if ( KMessageBox::questionYesNo(
00189                  0L,
00190                  i18n("During the previous startup, KNotify crashed while instantiating "
00191                       "KNotify. Do you want to try again or disable "
00192                       "aRts sound output?\n\n"
00193                       "If you choose to disable aRts output now, you can re-enable "
00194                       "it later or select an alternate sound player "
00195                       "in the System Notifications control panel."),
00196                  i18n("KNotify Problem"),
00197                  i18n("&Try Again"),
00198                  i18n("D&isable aRts Output"),
00199                  "KNotifyStartProgress",
00200                  0 /* don't call KNotify :) */
00201                  )
00202              == KMessageBox::No )
00203         {
00204             useArts = false;
00205             delete soundServer;
00206             soundServer = 0L;
00207             delete dispatcher;
00208             dispatcher = 0L;
00209         }
00210     }
00211 
00212     // when KNotify instantiation crashes, we know it the next start.
00213     config.writeEntry( "KNotify Init", false );
00214     config.writeEntry( "Use Arts", useArts );
00215     config.sync();
00216 
00217     // start notify service
00218     KNotify *notify = new KNotify( useArts );
00219 
00220     config.writeEntry( "KNotify Init", true );
00221     config.sync();
00222 
00223 #else
00224 
00225     // start notify service, without aRts
00226     KNotify *notify = new KNotify( false );
00227 
00228 #endif
00229 
00230     app.dcopClient()->setDefaultObject( "Notify" );
00231     app.dcopClient()->setDaemonMode( true );
00232     // kdDebug() << "knotify starting" << endl;
00233 
00234     int ret = app.exec();
00235     delete notify;
00236 #ifndef WITHOUT_ARTS
00237     delete soundServer;
00238     delete dispatcher;
00239 #endif
00240     return ret;
00241 }
00242 }// end extern "C"
00243 
00244 KNotify::KNotify( bool useArts )
00245     : QObject(), DCOPObject("Notify")
00246 {
00247     d = new KNotifyPrivate;
00248     d->globalEvents = new KConfig("knotify/eventsrc", true, false, "data");
00249     d->globalConfig = new KConfig("knotify.eventsrc", true, false);
00250     d->externalPlayerProc = 0;
00251     d->useArts = useArts;
00252     d->inStartup = true;
00253 #ifndef WITHOUT_ARTS
00254     d->playObjects.setAutoDelete(true);
00255     d->audioManager = 0;
00256     if( useArts )
00257     {
00258         connect( soundServer, SIGNAL( restartedServer() ), this, SLOT( restartedArtsd() ) );
00259         restartedArtsd(); //started allready need to initialize d->audioManager
00260     }
00261 #endif
00262 
00263     d->volume = 100;
00264 
00265     d->playTimer = 0;
00266 
00267     loadConfig();
00268 }
00269 
00270 KNotify::~KNotify()
00271 {
00272     reconfigure();
00273 
00274 #ifndef WITHOUT_ARTS
00275     d->playObjects.clear();
00276 
00277     delete d->globalEvents;
00278     delete d->globalConfig;
00279     delete d->externalPlayerProc;
00280     delete d->audioManager;
00281 #endif
00282     delete d;
00283 }
00284 
00285 
00286 void KNotify::loadConfig() {
00287     // load external player settings
00288     KConfig *kc = KGlobal::config();
00289     kc->setGroup("Misc");
00290     d->useExternal = kc->readBoolEntry( "Use external player", false );
00291     d->externalPlayer = kc->readPathEntry("External player");
00292 
00293     // try to locate a suitable player if none is configured
00294     if ( d->externalPlayer.isEmpty() ) {
00295         QStringList players;
00296         players << "wavplay" << "aplay" << "auplay";
00297         QStringList::Iterator it = players.begin();
00298         while ( d->externalPlayer.isEmpty() && it != players.end() ) {
00299             d->externalPlayer = KStandardDirs::findExe( *it );
00300             ++it;
00301         }
00302     }
00303 
00304     // load default volume
00305     d->volume = kc->readNumEntry( "Volume", 100 );
00306 }
00307 
00308 
00309 void KNotify::reconfigure()
00310 {
00311     kapp->config()->reparseConfiguration();
00312     loadConfig();
00313 
00314     // clear loaded config files
00315     d->globalConfig->reparseConfiguration();
00316     for ( QMapIterator<QString,KConfig*> it = d->configs.begin(); it != d->configs.end(); ++it )
00317         delete it.data();
00318     d->configs.clear();
00319 }
00320 
00321 
00322 void KNotify::notify(const QString &event, const QString &fromApp,
00323                      const QString &text, QString sound, QString file,
00324                      int present, int level)
00325 {
00326     notify( event, fromApp, text, sound, file, present, level, 0, 1 );
00327 }
00328 
00329 void KNotify::notify(const QString &event, const QString &fromApp,
00330                      const QString &text, QString sound, QString file,
00331                      int present, int level, int winId)
00332 {
00333     notify( event, fromApp, text, sound, file, present, level, winId, 1 );
00334 }
00335 
00336 void KNotify::notify(const QString &event, const QString &fromApp,
00337                      const QString &text, QString sound, QString file,
00338                      int present, int level, int winId, int eventId )
00339 {
00340     // kdDebug() << "event=" << event << " fromApp=" << fromApp << " text=" << text << " sound=" << sound <<
00341     //    " file=" << file << " present=" << present << " level=" << level <<  " winId=" << winId << " eventId=" << eventId << endl;
00342     if( d->inStartup ) {
00343         d->startupEvents += "(" + event + ":" + fromApp + ")";
00344     }
00345 
00346     QString commandline;
00347     KConfig *eventsFile = NULL;
00348     KConfig *configFile = NULL;
00349 
00350     // check for valid events
00351     if ( !event.isEmpty() ) {
00352 
00353         // get config file
00354         if ( d->events.contains( fromApp ) ) {
00355             eventsFile = d->events[fromApp];
00356         } else {
00357             eventsFile=new KConfig(locate("data", fromApp+"/eventsrc"),true,false);
00358             d->events.insert( fromApp, eventsFile );
00359         }
00360         if ( d->configs.contains( fromApp) ) {
00361             configFile = d->configs[fromApp];
00362         } else {
00363             configFile=new KConfig(fromApp+".eventsrc",true,false);
00364             d->configs.insert( fromApp, configFile );
00365         }
00366 
00367         if ( !eventsFile->hasGroup( event ) && isGlobal(event) )
00368         {
00369             eventsFile = d->globalEvents;
00370             configFile = d->globalConfig;
00371         }
00372 
00373         eventsFile->setGroup( event );
00374         configFile->setGroup( event );
00375 
00376         // get event presentation
00377         if ( present==-1 )
00378             present = configFile->readNumEntry( "presentation", -1 );
00379         if ( present==-1 )
00380             present = eventsFile->readNumEntry( "default_presentation", 0 );
00381 
00382         // get sound file name
00383         if( present & KNotifyClient::Sound ) {
00384             QString theSound = configFile->readPathEntry( "soundfile" );
00385             if ( theSound.isEmpty() )
00386                 theSound = eventsFile->readPathEntry( "default_sound" );
00387             if ( !theSound.isEmpty() )
00388                 sound = theSound;
00389         }
00390 
00391         // get log file name
00392         if( present & KNotifyClient::Logfile ) {
00393             QString theFile = configFile->readPathEntry( "logfile" );
00394             if ( theFile.isEmpty() )
00395                 theFile = eventsFile->readPathEntry( "default_logfile" );
00396             if ( !theFile.isEmpty() )
00397                 file = theFile;
00398         }
00399 
00400         // get default event level
00401         if( present & KNotifyClient::Messagebox )
00402             level = eventsFile->readNumEntry( "level", 0 );
00403 
00404         // get command line
00405         if (present & KNotifyClient::Execute ) {
00406             commandline = configFile->readPathEntry( "commandline" );
00407             if ( commandline.isEmpty() )
00408                 commandline = eventsFile->readPathEntry( "default_commandline" );
00409         }
00410     }
00411 
00412     // emit event
00413     if ( present & KNotifyClient::Sound ) // && QFile(sound).isReadable()
00414         notifyBySound( sound, fromApp, eventId );
00415 
00416     if ( present & KNotifyClient::Execute )
00417         notifyByExecute( commandline, event, fromApp, text, winId, eventId );
00418 
00419     if ( present & KNotifyClient::Logfile ) // && QFile(file).isWritable()
00420         notifyByLogfile( text, file );
00421 
00422     if ( present & KNotifyClient::Stderr )
00423         notifyByStderr( text );
00424 
00425     if ( present & KNotifyClient::Taskbar )
00426         notifyByTaskbar( checkWinId( fromApp, winId ));
00427 
00428     if ( present & KNotifyClient::PassivePopup )
00429         notifyByPassivePopup( text, fromApp, eventsFile, checkWinId( fromApp, winId ));
00430     else if ( present & KNotifyClient::Messagebox )
00431         notifyByMessagebox( text, level, checkWinId( fromApp, winId ));
00432 
00433     QByteArray qbd;
00434     QDataStream ds(qbd, IO_WriteOnly);
00435     ds << event << fromApp << text << sound << file << present << level
00436         << winId << eventId;
00437     emitDCOPSignal("notifySignal(QString,QString,QString,QString,QString,int,int,int,int)", qbd);
00438 
00439 }
00440 
00441 
00442 bool KNotify::notifyBySound( const QString &sound, const QString &appname, int eventId )
00443 {
00444     if (sound.isEmpty()) {
00445         soundFinished( eventId, NoSoundFile );
00446         return false;
00447     }
00448 
00449     bool external = d->useExternal && !d->externalPlayer.isEmpty();
00450     // get file name
00451     QString soundFile(sound);
00452     if ( QFileInfo(sound).isRelative() )
00453     {
00454         QString search = QString("%1/sounds/%2").arg(appname).arg(sound);
00455         soundFile = KGlobal::instance()->dirs()->findResource("data", search);
00456         if ( soundFile.isEmpty() )
00457             soundFile = locate( "sound", sound );
00458     }
00459     if ( soundFile.isEmpty() || isPlaying( soundFile ) )
00460     {
00461         soundFinished( eventId, soundFile.isEmpty() ? NoSoundFile : FileAlreadyPlaying );
00462         return false;
00463     }
00464 
00465 
00466     // kdDebug() << "KNotify::notifyBySound - trying to play file " << soundFile << endl;
00467 
00468     if (!external) {
00469         //If we disabled using aRts, just return,
00470         //(If we don't, we'll blow up accessing the null soundServer)
00471         if (!d->useArts)
00472         {
00473             soundFinished( eventId, NoSoundSupport );
00474             return false;
00475         }
00476 
00477 #ifndef WITHOUT_ARTS
00478         // play sound finally
00479         while( d->playObjects.count()>5 )
00480             abortFirstPlayObject();
00481 
00482         KDE::PlayObjectFactory factory(soundServer->server());
00483         if( d->audioManager )
00484             factory.setAudioManagerPlay( d->audioManager );
00485         KURL soundURL;
00486         soundURL.setPath(soundFile);
00487         KDE::PlayObject *playObject = factory.createPlayObject(soundURL, false);
00488 
00489         if (playObject->isNull())
00490         {
00491             soundFinished( eventId, NoSoundSupport );
00492             delete playObject;
00493             return false;
00494         }
00495 
00496         if ( d->volume != 100 )
00497         {
00498             // It works to access the playObject immediately because we don't allow
00499             // non-file URLs for sounds.
00500             Arts::StereoVolumeControl volumeControl = Arts::DynamicCast(soundServer->server().createObject("Arts::StereoVolumeControl"));
00501             Arts::PlayObject player = playObject->object();
00502             Arts::Synth_AMAN_PLAY ap = d->audioManager->amanPlay();
00503             if( ! volumeControl.isNull() && ! player.isNull() && ! ap.isNull() )
00504             {
00505                 volumeControl.scaleFactor( d->volume/100.0 );
00506 
00507                 ap.stop();
00508                 Arts::disconnect( player, "left", ap, "left" );
00509                 Arts::disconnect( player, "right", ap, "right" );
00510 
00511                 ap.start();
00512                 volumeControl.start();
00513 
00514                 Arts::connect(player,"left",volumeControl,"inleft");
00515                 Arts::connect(player,"right",volumeControl,"inright");
00516 
00517                 Arts::connect(volumeControl,"outleft",ap,"left");
00518                 Arts::connect(volumeControl,"outright",ap,"right");
00519 
00520                 player._addChild( volumeControl, "volume" );
00521             }
00522         }
00523 
00524         playObject->play();
00525         d->playObjects.append( playObject );
00526         d->playObjectEventMap.insert( playObject, eventId );
00527 
00528         if ( !d->playTimer )
00529         {
00530             d->playTimer = new QTimer( this );
00531             connect( d->playTimer, SIGNAL( timeout() ), SLOT( playTimeout() ) );
00532         }
00533         if ( !d->playTimer->isActive() )
00534             d->playTimer->start( 1000 );
00535 #endif
00536         return true;
00537 
00538     } else if(!d->externalPlayer.isEmpty()) {
00539         // use an external player to play the sound
00540         KProcess *proc = d->externalPlayerProc;
00541         if (!proc)
00542         {
00543            proc = d->externalPlayerProc = new KProcess;
00544            connect( proc, SIGNAL( processExited( KProcess * )),
00545                     SLOT( slotPlayerProcessExited( KProcess * )));
00546         }
00547         if (proc->isRunning())
00548         {
00549            soundFinished( eventId, PlayerBusy );
00550            return false; // Skip
00551         }
00552         proc->clearArguments();
00553         (*proc) << d->externalPlayer << QFile::encodeName( soundFile );
00554         d->externalPlayerEventId = eventId;
00555         proc->start(KProcess::NotifyOnExit);
00556         return true;
00557     }
00558 
00559     soundFinished( eventId, Unknown );
00560     return false;
00561 }
00562 
00563 bool KNotify::notifyByMessagebox(const QString &text, int level, WId winId)
00564 {
00565     // ignore empty messages
00566     if ( text.isEmpty() )
00567         return false;
00568 
00569     // display message box for specified event level
00570     switch( level ) {
00571     default:
00572     case KNotifyClient::Notification:
00573         KMessageBox::informationWId( winId, text, i18n("Notification"), 0, false );
00574         break;
00575     case KNotifyClient::Warning:
00576         KMessageBox::sorryWId( winId, text, i18n("Warning"), false );
00577         break;
00578     case KNotifyClient::Error:
00579         KMessageBox::errorWId( winId, text, i18n("Error"), false );
00580         break;
00581     case KNotifyClient::Catastrophe:
00582         KMessageBox::errorWId( winId, text, i18n("Catastrophe!"), false );
00583         break;
00584     }
00585 
00586     return true;
00587 }
00588 
00589 bool KNotify::notifyByPassivePopup( const QString &text,
00590                                     const QString &appName,
00591                                     KConfig* eventsFile,
00592                                     WId senderWinId )
00593 {
00594     KIconLoader iconLoader( appName );
00595     if ( eventsFile != NULL ) {
00596         KConfigGroup config( eventsFile, "!Global!" );
00597         QString iconName = config.readEntry( "IconName", appName );
00598         QPixmap icon = iconLoader.loadIcon( iconName, KIcon::Small );
00599         QString title = config.readEntry( "Comment", appName );
00600         KPassivePopup::message(title, text, icon, senderWinId);
00601     } else
00602         kdError() << "No events for app " << appName << "defined!" <<endl;
00603 
00604     return true;
00605 }
00606 
00607 bool KNotify::notifyByExecute(const QString &command, const QString& event,
00608                               const QString& fromApp, const QString& text,
00609                               int winId, int eventId) {
00610     if (!command.isEmpty()) {
00611     // kdDebug() << "executing command '" << command << "'" << endl;
00612         QMap<QChar,QString> subst;
00613         subst.insert( 'e', event );
00614         subst.insert( 'a', fromApp );
00615         subst.insert( 's', text );
00616         subst.insert( 'w', QString::number( winId ));
00617         subst.insert( 'i', QString::number( eventId ));
00618         QString execLine = KMacroExpander::expandMacrosShellQuote( command, subst );
00619         if ( execLine.isEmpty() )
00620             execLine = command; // fallback
00621 
00622     KProcess p;
00623     p.setUseShell(true);
00624     p << execLine;
00625     p.start(KProcess::DontCare);
00626     return true;
00627     }
00628     return false;
00629 }
00630 
00631 
00632 bool KNotify::notifyByLogfile(const QString &text, const QString &file)
00633 {
00634     // ignore empty messages
00635     if ( text.isEmpty() )
00636         return true;
00637 
00638     // open file in append mode
00639     QFile logFile(file);
00640     if ( !logFile.open(IO_WriteOnly | IO_Append) )
00641         return false;
00642 
00643     // append msg
00644     QTextStream strm( &logFile );
00645     strm << "- KNotify " << QDateTime::currentDateTime().toString() << ": ";
00646     strm << text << endl;
00647 
00648     // close file
00649     logFile.close();
00650     return true;
00651 }
00652 
00653 bool KNotify::notifyByStderr(const QString &text)
00654 {
00655     // ignore empty messages
00656     if ( text.isEmpty() )
00657         return true;
00658 
00659     // open stderr for output
00660     QTextStream strm( stderr, IO_WriteOnly );
00661 
00662     // output msg
00663     strm << "KNotify " << QDateTime::currentDateTime().toString() << ": ";
00664     strm << text << endl;
00665 
00666     return true;
00667 }
00668 
00669 bool KNotify::notifyByTaskbar( WId win )
00670 {
00671     if( win == 0 )
00672         return false;
00673     KWin::demandAttention( win );
00674     return true;
00675 }
00676 
00677 bool KNotify::isGlobal(const QString &eventname)
00678 {
00679     return d->globalEvents->hasGroup( eventname );
00680 }
00681 
00682 void KNotify::setVolume( int volume )
00683 {
00684     if ( volume<0 ) volume=0;
00685     if ( volume>=100 ) volume=100;
00686     d->volume = volume;
00687 }
00688 
00689 void KNotify::playTimeout()
00690 {
00691 #ifndef WITHOUT_ARTS
00692     for ( QPtrListIterator< KDE::PlayObject > it(d->playObjects); *it;)
00693     {
00694         QPtrListIterator< KDE::PlayObject > current = it;
00695         ++it;
00696         if ( (*current)->state() != Arts::posPlaying )
00697         {
00698             QMap<KDE::PlayObject*,int>::Iterator eit = d->playObjectEventMap.find( *current );
00699             if ( eit != d->playObjectEventMap.end() )
00700             {
00701                 soundFinished( *eit, PlayedOK );
00702                 d->playObjectEventMap.remove( eit );
00703             }
00704             d->playObjects.remove( current );
00705         }
00706     }
00707     if ( !d->playObjects.count() )
00708         d->playTimer->stop();
00709 #endif
00710 }
00711 
00712 bool KNotify::isPlaying( const QString& soundFile ) const
00713 {
00714 #ifndef WITHOUT_ARTS
00715     for ( QPtrListIterator< KDE::PlayObject > it(d->playObjects); *it; ++it)
00716     {
00717         if ( (*it)->mediaName() == soundFile )
00718             return true;
00719     }
00720 #endif
00721     return false;
00722 }
00723 
00724 void KNotify::slotPlayerProcessExited( KProcess *proc )
00725 {
00726     soundFinished( d->externalPlayerEventId,
00727                    (proc->normalExit() && proc->exitStatus() == 0) ? PlayedOK : Unknown );
00728 }
00729 
00730 void KNotify::abortFirstPlayObject()
00731 {
00732 #ifndef WITHOUT_ARTS
00733     QMap<KDE::PlayObject*,int>::Iterator it = d->playObjectEventMap.find( d->playObjects.getFirst() );
00734     if ( it != d->playObjectEventMap.end() )
00735     {
00736         soundFinished( it.data(), Aborted );
00737         d->playObjectEventMap.remove( it );
00738     }
00739     d->playObjects.removeFirst();
00740 #endif
00741 }
00742 
00743 void KNotify::soundFinished( int eventId, PlayingFinishedStatus reason )
00744 {
00745     QByteArray data;
00746     QDataStream stream( data, IO_WriteOnly );
00747     stream << eventId << (int) reason;
00748 
00749     DCOPClient::mainClient()->emitDCOPSignal( "KNotify", "playingFinished(int,int)", data );
00750 }
00751 
00752 WId KNotify::checkWinId( const QString &appName, WId senderWinId )
00753 {
00754     if ( senderWinId == 0 )
00755     {
00756         QCString senderId = kapp->dcopClient()->senderId();
00757         QCString compare = (appName + "-mainwindow").latin1();
00758         int len = compare.length();
00759         // kdDebug() << "notifyByPassivePopup: appName=" << appName << " sender=" << senderId << endl;
00760 
00761         QCStringList objs = kapp->dcopClient()->remoteObjects( senderId );
00762         for (QCStringList::ConstIterator it = objs.begin(); it != objs.end(); ++it ) {
00763             QCString obj( *it );
00764             if ( obj.left(len) == compare) {
00765                 // kdDebug( ) << "found " << obj << endl;
00766                 QCString replyType;
00767                 QByteArray data, replyData;
00768 
00769                 if ( kapp->dcopClient()->call(senderId, obj, "getWinID()", data, replyType, replyData) ) {
00770                     QDataStream answer(replyData, IO_ReadOnly);
00771                     if (replyType == "int") {
00772                         answer >> senderWinId;
00773                         // kdDebug() << "SUCCESS, found getWinID(): type='" << QString(replyType)
00774                         //      << "' senderWinId=" << senderWinId << endl;
00775                     }
00776         }
00777             }
00778         }
00779     }
00780     return senderWinId;
00781 }
00782 
00783 void KNotify::restartedArtsd()
00784 {
00785 #ifndef WITHOUT_ARTS
00786     delete d->audioManager;
00787     d->audioManager = new KAudioManagerPlay( soundServer );
00788     d->audioManager->setTitle( i18n( "KDE System Notifications" ) );
00789     d->audioManager->setAutoRestoreID( "KNotify Aman Play" );
00790 #endif
00791 }
00792 
00793 void KNotify::sessionReady()
00794 {
00795     if( d->inStartup && !d->startupEvents.isEmpty())
00796         kdDebug() << "There were knotify events while startup:" << d->startupEvents << endl;
00797     d->inStartup = false;
00798 }
00799 
00800 // vim: sw=4 sts=4 ts=8 et
KDE Home | KDE Accessibility Home | Description of Access Keys