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