knotifyclient.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Charles Samuels <charles@altair.dhs.org> 00003 2000 Malte Starostik <starosti@zedat.fu-berlin.de> 00004 2000,2003 Carsten Pfeiffer <pfeiffer@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License version 2 as published by the Free Software Foundation. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "knotifyclient.h" 00022 00023 #include <tqdatastream.h> 00024 #include <tqptrstack.h> 00025 00026 #include <kapplication.h> 00027 #include <kstandarddirs.h> 00028 #include <kapplication.h> 00029 #include <kconfig.h> 00030 #include <dcopclient.h> 00031 #include <kdebug.h> 00032 #include <kstaticdeleter.h> 00033 00034 static const char daemonName[] = "knotify"; 00035 00036 static bool canAvoidStartupEvent( const TQString& event, const TQString& appname, int present ) 00037 { 00038 static bool checkAvoid = true; 00039 if( !checkAvoid ) 00040 return false; 00041 if(( appname != "kwin" && appname != "ksmserver" ) || present > 0 ) { 00042 checkAvoid = false; 00043 return false; 00044 } 00045 // startkde event is in global events file 00046 static KConfig* configfile = appname != "ksmserver" 00047 ? new KConfig( appname + ".eventsrc", true, false ) 00048 : new KConfig( "knotify.eventsrc", true, false ); 00049 static KConfig* eventsfile = appname != "ksmserver" 00050 ? new KConfig( appname + "/eventsrc", true, false, "data" ) 00051 : new KConfig( "knotify/eventsrc", true, false, "data" ); 00052 configfile->setGroup( event ); 00053 eventsfile->setGroup( event ); 00054 int ev1 = configfile->readNumEntry( "presentation", -2 ); 00055 int ev2 = eventsfile->readNumEntry( "default_presentation", -2 ); 00056 if(( ev1 == -2 && ev2 == -2 ) // unknown 00057 || ev1 > 0 // configured to have presentation 00058 || ( ev1 == -2 && ev2 > 0 )) { // not configured, has default presentation 00059 checkAvoid = false; 00060 return false; 00061 } 00062 return true; 00063 } 00064 00065 static int sendNotifyEvent(const TQString &message, const TQString &text, 00066 int present, int level, const TQString &sound, 00067 const TQString &file, int winId ) 00068 { 00069 if (!kapp) return 0; 00070 00071 DCOPClient *client=kapp->dcopClient(); 00072 if (!client->isAttached()) 00073 { 00074 client->attach(); 00075 if (!client->isAttached()) 00076 return 0; 00077 } 00078 00079 TQString appname = KNotifyClient::instance()->instanceName(); 00080 00081 if( canAvoidStartupEvent( message, appname, present )) 00082 return -1; // done "successfully" - there will be no event presentation 00083 00084 int uniqueId = kMax( 1, kapp->random() ); // must not be 0 -- means failure! 00085 00086 // knotify daemon needs toplevel window 00087 TQWidget* widget = TQT_TQWIDGET(TQWidget::find( (WId)winId )); 00088 if( widget ) 00089 winId = (int)widget->topLevelWidget()->winId(); 00090 00091 TQByteArray data; 00092 TQDataStream ds(data, IO_WriteOnly); 00093 ds << message << appname << text << sound << file << present << level 00094 << winId << uniqueId; 00095 00096 if ( !KNotifyClient::startDaemon() ) 00097 return 0; 00098 00099 if ( client->send(daemonName, "Notify", "notify(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", data) ) 00100 { 00101 return uniqueId; 00102 } 00103 00104 return 0; 00105 } 00106 00107 int KNotifyClient::event( StandardEvent type, const TQString& text ) 00108 { 00109 return event( 0, type, text ); 00110 } 00111 00112 int KNotifyClient::event(const TQString &message, const TQString &text) 00113 { 00114 return event(0, message, text); 00115 } 00116 00117 int KNotifyClient::userEvent(const TQString &text, int present, int level, 00118 const TQString &sound, const TQString &file) 00119 { 00120 return userEvent( 0, text, present, level, sound, file ); 00121 } 00122 00123 00124 int KNotifyClient::event( int winId, StandardEvent type, const TQString& text ) 00125 { 00126 TQString message; 00127 switch ( type ) { 00128 case cannotOpenFile: 00129 message = TQString::fromLatin1("cannotopenfile"); 00130 break; 00131 case warning: 00132 message = TQString::fromLatin1("warning"); 00133 break; 00134 case fatalError: 00135 message = TQString::fromLatin1("fatalerror"); 00136 break; 00137 case catastrophe: 00138 message = TQString::fromLatin1("catastrophe"); 00139 break; 00140 case notification: // fall through 00141 default: 00142 message = TQString::fromLatin1("notification"); 00143 break; 00144 } 00145 00146 return sendNotifyEvent( message, text, Default, Default, 00147 TQString::null, TQString::null, winId ); 00148 } 00149 00150 int KNotifyClient::event(int winId, const TQString &message, 00151 const TQString &text) 00152 { 00153 return sendNotifyEvent(message, text, Default, Default, TQString::null, TQString::null, winId); 00154 } 00155 00156 int KNotifyClient::userEvent(int winId, const TQString &text, int present, 00157 int level, 00158 const TQString &sound, const TQString &file) 00159 { 00160 return sendNotifyEvent(TQString::null, text, present, level, sound, file, winId); 00161 } 00162 00163 int KNotifyClient::getPresentation(const TQString &eventname) 00164 { 00165 int present; 00166 if (eventname.isEmpty()) return Default; 00167 00168 KConfig eventsfile( KNotifyClient::instance()->instanceName()+".eventsrc", true, false); 00169 eventsfile.setGroup(eventname); 00170 00171 present=eventsfile.readNumEntry("presentation", -1); 00172 00173 return present; 00174 } 00175 00176 TQString KNotifyClient::getFile(const TQString &eventname, int present) 00177 { 00178 if (eventname.isEmpty()) return TQString::null; 00179 00180 KConfig eventsfile( KNotifyClient::instance()->instanceName()+".eventsrc", true, false); 00181 eventsfile.setGroup(eventname); 00182 00183 switch (present) 00184 { 00185 case (Sound): 00186 return eventsfile.readPathEntry("soundfile"); 00187 case (Logfile): 00188 return eventsfile.readPathEntry("logfile"); 00189 } 00190 00191 return TQString::null; 00192 } 00193 00194 int KNotifyClient::getDefaultPresentation(const TQString &eventname) 00195 { 00196 int present; 00197 if (eventname.isEmpty()) return Default; 00198 00199 KConfig eventsfile( KNotifyClient::instance()->instanceName()+"/eventsrc", true, false, "data"); 00200 eventsfile.setGroup(eventname); 00201 00202 present=eventsfile.readNumEntry("default_presentation", -1); 00203 00204 return present; 00205 } 00206 00207 TQString KNotifyClient::getDefaultFile(const TQString &eventname, int present) 00208 { 00209 if (eventname.isEmpty()) return TQString::null; 00210 00211 KConfig eventsfile( KNotifyClient::instance()->instanceName()+"/eventsrc", true, false, "data"); 00212 eventsfile.setGroup(eventname); 00213 00214 switch (present) 00215 { 00216 case (Sound): 00217 return eventsfile.readPathEntry("default_sound"); 00218 case (Logfile): 00219 return eventsfile.readPathEntry("default_logfile"); 00220 } 00221 00222 return TQString::null; 00223 } 00224 00225 bool KNotifyClient::startDaemon() 00226 { 00227 static bool firstTry = true; 00228 if (!kapp->dcopClient()->isApplicationRegistered(daemonName)) { 00229 if( firstTry ) { 00230 firstTry = false; 00231 return KApplication::startServiceByDesktopName(daemonName) == 0; 00232 } 00233 return false; 00234 } 00235 return true; 00236 } 00237 00238 00239 void KNotifyClient::beep(const TQString& reason) 00240 { 00241 if ( !kapp || KNotifyClient::Instance::currentInstance()->useSystemBell() ) { 00242 TQApplication::beep(); 00243 return; 00244 } 00245 00246 DCOPClient *client=kapp->dcopClient(); 00247 if (!client->isAttached()) 00248 { 00249 client->attach(); 00250 if (!client->isAttached() || !client->isApplicationRegistered(daemonName)) 00251 { 00252 TQApplication::beep(); 00253 return; 00254 } 00255 } 00256 // The kaccess daemon handles visual and other audible beeps 00257 if ( client->isApplicationRegistered( "kaccess" ) ) 00258 { 00259 TQApplication::beep(); 00260 return; 00261 } 00262 00263 KNotifyClient::event(KNotifyClient::notification, reason); 00264 } 00265 00266 00267 KInstance * KNotifyClient::instance() { 00268 return KNotifyClient::Instance::current(); 00269 } 00270 00271 00272 class KNotifyClient::InstanceStack 00273 { 00274 public: 00275 InstanceStack() { m_defaultInstance = 0; } 00276 virtual ~InstanceStack() { delete m_defaultInstance; } 00277 void push(Instance *instance) { m_instances.push(instance); } 00278 00279 void pop(Instance *instance) 00280 { 00281 if (m_instances.top() == instance) 00282 m_instances.pop(); 00283 else if (!m_instances.isEmpty()) 00284 { 00285 kdWarning(160) << "Tried to remove an Instance that is not the current," << endl; 00286 kdWarning(160) << "Resetting to the main KApplication." << endl; 00287 m_instances.clear(); 00288 } 00289 else 00290 kdWarning(160) << "Tried to remove an Instance, but the stack was empty." << endl; 00291 } 00292 00293 Instance *currentInstance() 00294 { 00295 if (m_instances.isEmpty()) 00296 { 00297 m_defaultInstance = new Instance(kapp); 00298 } 00299 return m_instances.top(); 00300 } 00301 00302 private: 00303 TQPtrStack<Instance> m_instances; 00304 Instance *m_defaultInstance; 00305 }; 00306 00307 KNotifyClient::InstanceStack * KNotifyClient::Instance::s_instances = 0L; 00308 static KStaticDeleter<KNotifyClient::InstanceStack > instancesDeleter; 00309 00310 struct KNotifyClient::InstancePrivate 00311 { 00312 KInstance *instance; 00313 bool useSystemBell; 00314 }; 00315 00316 KNotifyClient::Instance::Instance(KInstance *instance) 00317 { 00318 d = new InstancePrivate; 00319 d->instance = instance; 00320 instances()->push(this); 00321 00322 KConfig *config = instance->config(); 00323 KConfigGroupSaver cs( config, "General" ); 00324 d->useSystemBell = config->readBoolEntry( "UseSystemBell", false ); 00325 } 00326 00327 KNotifyClient::Instance::~Instance() 00328 { 00329 if (s_instances) 00330 s_instances->pop(this); 00331 delete d; 00332 } 00333 00334 KNotifyClient::InstanceStack *KNotifyClient::Instance::instances() 00335 { 00336 if (!s_instances) 00337 instancesDeleter.setObject(s_instances, new InstanceStack); 00338 return s_instances; 00339 } 00340 00341 bool KNotifyClient::Instance::useSystemBell() const 00342 { 00343 return d->useSystemBell; 00344 } 00345 00346 00347 // static methods 00348 00349 // We always return a valid KNotifyClient::Instance here. If no special one 00350 // is available, we have a default-instance with kapp as KInstance. 00351 // We make sure to always have that default-instance in the stack, because 00352 // the stack might have gotten cleared in the destructor. 00353 // We can't use QStack::setAutoDelete( true ), because no instance besides 00354 // our default instance is owned by us. 00355 KNotifyClient::Instance * KNotifyClient::Instance::currentInstance() 00356 { 00357 return instances()->currentInstance(); 00358 } 00359 00360 KInstance *KNotifyClient::Instance::current() 00361 { 00362 return currentInstance()->d->instance; 00363 }