00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "notifybypopup.h"
00022 #include "knotifyconfig.h"
00023
00024 #include <kdebug.h>
00025 #include <kpassivepopup.h>
00026 #include <kiconloader.h>
00027 #include <kdialog.h>
00028 #include <khbox.h>
00029 #include <kvbox.h>
00030
00031 #include <QLabel>
00032 #include <QTextDocument>
00033 #include <QApplication>
00034 #include <QDesktopWidget>
00035 #include <QDBusConnection>
00036 #include <QDBusConnectionInterface>
00037 #include <kconfiggroup.h>
00038
00039 static const QString dbusServiceName = "org.kde.VisualNotifications";
00040 static const QString dbusInterfaceName = "org.kde.VisualNotifications";
00041 static const QString dbusPath = "/VisualNotifications";
00042
00043 NotifyByPopup::NotifyByPopup(QObject *parent)
00044 : KNotifyPlugin(parent) , m_animationTimer(0), m_dbusServiceExists(false)
00045 {
00046 QRect screen = QApplication::desktop()->availableGeometry();
00047 m_nextPosition = screen.top();
00048
00049
00050 QDBusConnectionInterface* interface = QDBusConnection::sessionBus().interface();
00051 m_dbusServiceExists = interface && interface->isServiceRegistered(dbusServiceName);
00052
00053 if( m_dbusServiceExists )
00054 kDebug(300) << "using" << dbusServiceName << "for popups";
00055
00056
00057 connect(interface, SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
00058 SLOT(slotServiceOwnerChanged(const QString&, const QString&, const QString&)));
00059 }
00060
00061
00062 NotifyByPopup::~NotifyByPopup()
00063 {
00064 foreach(KPassivePopup *p,m_popups)
00065 p->deleteLater();
00066 }
00067
00068 void NotifyByPopup::notify( int id, KNotifyConfig * config )
00069 {
00070 kDebug(300) << id;
00071
00072
00073
00074 if(m_dbusServiceExists)
00075 {
00076 sendNotificationDBus(id, 0, config);
00077 return;
00078 }
00079
00080 if(m_popups.contains(id))
00081 {
00082
00083 finish(id);
00084 return;
00085 }
00086
00087 KPassivePopup *pop = new KPassivePopup( config->winId );
00088 m_popups[id]=pop;
00089 fillPopup(pop,id,config);
00090 QRect screen = QApplication::desktop()->availableGeometry();
00091 pop->setAutoDelete( true );
00092 connect(pop, SIGNAL(destroyed()) , this, SLOT(slotPopupDestroyed()) );
00093
00094
00095
00096 QString timeoutStr = config->readEntry( "Timeout" );
00097 pop->setTimeout( !timeoutStr.isEmpty() ? timeoutStr.toInt() : 0 );
00098
00099 pop->show(QPoint(screen.left() + screen.width()/2 , m_nextPosition));
00100 m_nextPosition+=pop->height();
00101 }
00102
00103 void NotifyByPopup::slotPopupDestroyed( )
00104 {
00105 const QObject *s=sender();
00106 if(!s)
00107 return;
00108 QMap<int,KPassivePopup*>::iterator it;
00109 for(it=m_popups.begin() ; it!=m_popups.end(); ++it )
00110 {
00111 QObject *o=it.value();
00112 if(o && o == s)
00113 {
00114 finish(it.key());
00115 m_popups.remove(it.key());
00116 break;
00117 }
00118 }
00119
00120
00121 if(!m_animationTimer)
00122 m_animationTimer = startTimer(10);
00123 }
00124
00125 void NotifyByPopup::timerEvent(QTimerEvent * event)
00126 {
00127 if(event->timerId() != m_animationTimer)
00128 return KNotifyPlugin::timerEvent(event);
00129
00130 bool cont=false;
00131 QRect screen = QApplication::desktop()->availableGeometry();
00132 m_nextPosition = screen.top();
00133 foreach(KPassivePopup *pop,m_popups)
00134 {
00135 int posy=pop->pos().y();
00136 if(posy > m_nextPosition)
00137 {
00138 posy=qMax(posy-5,m_nextPosition);
00139 m_nextPosition = posy + pop->height();
00140 cont = cont || posy != m_nextPosition;
00141 pop->move(pop->pos().x(),posy);
00142 }
00143 else
00144 m_nextPosition += pop->height();
00145 }
00146 if(!cont)
00147 {
00148 killTimer(m_animationTimer);
00149 m_animationTimer = 0;
00150 }
00151 }
00152
00153 void NotifyByPopup::slotLinkClicked( const QString &adr )
00154 {
00155 unsigned int id=adr.section("/" , 0 , 0).toUInt();
00156 unsigned int action=adr.section("/" , 1 , 1).toUInt();
00157
00158
00159
00160 if(id==0 || action==0)
00161 return;
00162
00163 emit actionInvoked(id,action);
00164 }
00165
00166 void NotifyByPopup::close( int id )
00167 {
00168
00169
00170 if( m_dbusServiceExists)
00171 {
00172 closeNotificationDBus(id);
00173 return;
00174 }
00175
00176 delete m_popups[id];
00177 m_popups.remove(id);
00178 }
00179
00180 void NotifyByPopup::update(int id, KNotifyConfig * config)
00181 {
00182
00183
00184 if( m_dbusServiceExists)
00185 {
00186 sendNotificationDBus(id, id, config);
00187 return;
00188 }
00189
00190 if(!m_popups.contains(id))
00191 return;
00192 KPassivePopup *p=m_popups[id];
00193 fillPopup(p, id, config);
00194 }
00195
00196 void NotifyByPopup::fillPopup(KPassivePopup *pop,int id,KNotifyConfig * config)
00197 {
00198 QString appCaption, iconName;
00199 getAppCaptionAndIconName(config, &appCaption, &iconName);
00200
00201 KIconLoader iconLoader(iconName);
00202 QPixmap appIcon = iconLoader.loadIcon( iconName, KIconLoader::Small );
00203
00204 KVBox *vb = pop->standardView( appCaption , config->pix.isNull() ? config->text : QString() , appIcon );
00205 KVBox *vb2 = vb;
00206
00207 if(!config->pix.isNull())
00208 {
00209 const QPixmap &pix=config->pix;
00210 KHBox *hb = new KHBox(vb);
00211 hb->setSpacing(KDialog::spacingHint());
00212 QLabel *pil=new QLabel(hb);
00213 pil->setPixmap( config->pix );
00214 pil->setScaledContents(true);
00215 if(pix.height() > 80 && pix.height() > pix.width() )
00216 {
00217 pil->setMaximumHeight(80);
00218 pil->setMaximumWidth(80*pix.width()/pix.height());
00219 }
00220 else if(pix.width() > 80 && pix.height() <= pix.width())
00221 {
00222 pil->setMaximumWidth(80);
00223 pil->setMaximumHeight(80*pix.height()/pix.width());
00224 }
00225 vb=new KVBox(hb);
00226 QLabel *msg = new QLabel( config->text, vb );
00227 msg->setAlignment( Qt::AlignLeft );
00228 }
00229
00230
00231 if ( !config->actions.isEmpty() )
00232 {
00233 QString linkCode=QString::fromLatin1("<p align=\"right\">");
00234 int i=0;
00235 foreach ( const QString & it , config->actions )
00236 {
00237 i++;
00238 linkCode+=QString::fromLatin1(" <a href=\"%1/%2\">%3</a> ").arg( id ).arg( i ).arg( Qt::escape(it) );
00239 }
00240 linkCode+=QString::fromLatin1("</p>");
00241 QLabel *link = new QLabel(linkCode , vb );
00242 link->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
00243 link->setOpenExternalLinks(false);
00244
00245 QObject::connect(link, SIGNAL(linkActivated(const QString &)), this, SLOT(slotLinkClicked(const QString& ) ) );
00246 QObject::connect(link, SIGNAL(linkActivated(const QString &)), pop, SLOT(hide()));
00247 }
00248
00249 pop->setView( vb2 );
00250 }
00251
00252 void NotifyByPopup::slotServiceOwnerChanged( const QString & serviceName,
00253 const QString & oldOwner, const QString & newOwner )
00254 {
00255 if(serviceName == dbusServiceName)
00256 {
00257 if(oldOwner.isEmpty())
00258 {
00259
00260
00261
00262
00263
00264
00265
00266 foreach ( int id, m_popups.keys() )
00267 delete m_popups.value(id,0);
00268 m_popups.clear();
00269
00270 m_dbusServiceExists = true;
00271 kDebug(300) << dbusServiceName << " was registered on bus, now using it to show popups";
00272
00273
00274 m_idMap.clear();
00275
00276
00277 bool connected = QDBusConnection::sessionBus().connect(QString(),
00278 dbusPath,
00279 dbusInterfaceName,
00280 "ActionInvoked",
00281 this,
00282 SLOT(slotDBusNotificationActionInvoked(uint,const QString&)));
00283 if (!connected) {
00284 kDebug(300) << "warning: failed to connect to ActionInvoked dbus signal";
00285 }
00286
00287 connected = QDBusConnection::sessionBus().connect(QString(),
00288 dbusPath,
00289 dbusInterfaceName,
00290 "NotificationClosed",
00291 this,
00292 SLOT(slotDBusNotificationClosed(uint,uint)));
00293 if (!connected) {
00294 kDebug(300) << "warning: failed to connect to NotificationClosed dbus signal";
00295 }
00296 }
00297 if(newOwner.isEmpty())
00298 {
00299 m_dbusServiceExists = false;
00300
00301
00302 foreach (int id, m_idMap.keys()) {
00303 finished(id);
00304 }
00305 m_idMap.clear();
00306 kDebug(300) << dbusServiceName << " was unregistered from bus, using passive popups from now on";
00307 }
00308 }
00309 }
00310
00311 void NotifyByPopup::slotDBusNotificationActionInvoked(uint dbus_id, const QString& actKey)
00312 {
00313
00314 int id = m_idMap.key(dbus_id, 0);
00315 if (id == 0) {
00316 kDebug(300) << "failed to find knotify id for dbus_id" << dbus_id;
00317 return;
00318 }
00319 kDebug(300) << "action" << actKey << "invoked for notification " << id;
00320
00321 slotLinkClicked( QString("%1/%2").arg(id).arg(actKey) );
00322
00323
00324 closeNotificationDBus(id);
00325 }
00326
00327 void NotifyByPopup::slotDBusNotificationClosed(uint dbus_id, uint reason)
00328 {
00329 Q_UNUSED(reason)
00330
00331 int id = m_idMap.key(dbus_id, 0);
00332 if (id == 0) {
00333 kDebug(300) << "failed to find knotify id for dbus_id" << dbus_id;
00334 return;
00335 }
00336
00337 finished(id);
00338 }
00339
00340 void NotifyByPopup::getAppCaptionAndIconName(KNotifyConfig *config, QString *appCaption, QString *iconName)
00341 {
00342 KConfigGroup globalgroup(&(*config->eventsfile), "Global");
00343 *appCaption = globalgroup.readEntry("Name", globalgroup.readEntry("Comment", config->appname));
00344 *iconName = globalgroup.readEntry("IconName", config->appname);
00345 }
00346
00347 void NotifyByPopup::sendNotificationDBus(int id, int replacesId, KNotifyConfig* config)
00348 {
00349 QDBusMessage m = QDBusMessage::createMethodCall( dbusServiceName, dbusPath, dbusInterfaceName, "Notify" );
00350
00351
00352
00353 QString timeoutStr = config->readEntry( "Timeout" );
00354 int timeout = !timeoutStr.isEmpty() ? timeoutStr.toInt() : 0;
00355
00356
00357 if (timeout == 0) {
00358
00359
00360
00361
00362 bool persistent = (config->readEntry("Persistent") == "true" ||
00363 config->readEntry("Persistant") == "true");
00364 if (!persistent) {
00365 timeout = 6*1000;
00366 }
00367 }
00368
00369 QList<QVariant> args;
00370
00371
00372 uint dbus_replaces_id = 0;
00373 if (replacesId != 0 ) {
00374 dbus_replaces_id = m_idMap.value(replacesId, 0);
00375 }
00376
00377 QString appCaption, iconName;
00378 getAppCaptionAndIconName(config, &appCaption, &iconName);
00379
00380 args.append( appCaption );
00381 args.append( dbus_replaces_id );
00382 args.append( config->eventid );
00383 args.append( iconName );
00384 args.append( QString());
00385 args.append( config->text );
00386
00387
00388
00389
00390
00391 QStringList actionList;
00392 int actId = 0;
00393 foreach (const QString& actName, config->actions) {
00394 actId++;
00395 actionList.append(QString::number(actId));
00396 actionList.append(actName);
00397 }
00398
00399 args.append( actionList );
00400 args.append( QVariantMap() );
00401 args.append( timeout );
00402
00403 m.setArguments( args );
00404 QDBusMessage replyMsg = QDBusConnection::sessionBus().call(m);
00405 if(replyMsg.type() == QDBusMessage::ReplyMessage) {
00406 if (!replyMsg.arguments().isEmpty()) {
00407 uint dbus_id = replyMsg.arguments().at(0).toUInt();
00408 m_idMap.insert(id, dbus_id);
00409 kDebug() << "mapping knotify id to dbus id:"<< id << "=>" << dbus_id;
00410 } else {
00411 kDebug() << "error: received reply with no arguments";
00412 }
00413 } else if (replyMsg.type() == QDBusMessage::ErrorMessage) {
00414 kDebug() << "error: failed to send dbus message";
00415 } else {
00416 kDebug() << "unexpected reply type";
00417 }
00418 }
00419
00420 void NotifyByPopup::closeNotificationDBus(int id)
00421 {
00422 uint dbus_id = m_idMap.value(id, 0);
00423 if (dbus_id == 0) {
00424 kDebug() << "not found dbus id to close";
00425 return;
00426 }
00427
00428 QDBusMessage m = QDBusMessage::createMethodCall( dbusServiceName, dbusPath,
00429 dbusInterfaceName, "CloseNotification" );
00430 QList<QVariant> args;
00431 args.append( dbus_id );
00432 m.setArguments( args );
00433 bool queued = QDBusConnection::sessionBus().send(m);
00434 if(!queued)
00435 {
00436 kDebug() << "warning: failed to queue dbus message";
00437 }
00438 }
00439
00440 #include "notifybypopup.moc"