summaryrefslogtreecommitdiffstats
path: root/kopete/libkopete/knotification.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/libkopete/knotification.cpp')
-rw-r--r--kopete/libkopete/knotification.cpp533
1 files changed, 533 insertions, 0 deletions
diff --git a/kopete/libkopete/knotification.cpp b/kopete/libkopete/knotification.cpp
new file mode 100644
index 00000000..3749c21c
--- /dev/null
+++ b/kopete/libkopete/knotification.cpp
@@ -0,0 +1,533 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Olivier Goffart <ogoffart @ kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "knotification.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <knotifyclient.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+#include <kpassivepopup.h>
+#include <kactivelabel.h>
+#include <kprocess.h>
+#include <kdialog.h>
+#include <kmacroexpander.h>
+#include <kwin.h>
+
+
+#include <qvbox.h>
+#include <dcopclient.h>
+#include <qcstring.h>
+#include <qguardedptr.h>
+#include <qstylesheet.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <qtabwidget.h>
+
+
+
+//TODO, make the KNotification aware of the systemtray.
+#include "kopeteuiglobal.h"
+static WId checkWinId( const QString &/*appName*/, WId senderWinId )
+{
+ if(senderWinId==0)
+ senderWinId=Kopete::UI::Global::sysTrayWId();
+
+ return senderWinId;
+}
+
+
+struct KNotification::Private
+{
+ QWidget *widget;
+ QString text;
+ QStringList actions;
+ int level;
+};
+
+KNotification::KNotification(QObject *parent) :
+ QObject(parent) , d(new Private)
+{
+ m_linkClicked = false;
+}
+
+KNotification::~KNotification()
+{
+ delete d;
+}
+
+
+void KNotification::notifyByExecute(const QString &command, const QString& event,
+ const QString& fromApp, const QString& text,
+ int winId, int eventId)
+{
+ if (!command.isEmpty())
+ {
+ // kdDebug() << "executing command '" << command << "'" << endl;
+ QMap<QChar,QString> subst;
+ subst.insert( 'e', event );
+ subst.insert( 'a', fromApp );
+ subst.insert( 's', text );
+ subst.insert( 'w', QString::number( winId ));
+ subst.insert( 'i', QString::number( eventId ));
+ QString execLine = KMacroExpander::expandMacrosShellQuote( command, subst );
+ if ( execLine.isEmpty() )
+ execLine = command; // fallback
+
+ KProcess p;
+ p.setUseShell(true);
+ p << execLine;
+ p.start(KProcess::DontCare);
+// return true;
+ }
+ //return false;
+}
+
+
+void KNotification::notifyByMessagebox()
+{
+ // ignore empty messages
+ if ( d->text.isEmpty() )
+ return;
+
+ QString action=d->actions[0];
+ WId winId=d->widget ? d->widget->topLevelWidget()->winId() : 0;
+
+ if( action.isEmpty())
+ {
+ // display message box for specified event level
+ switch( d->level )
+ {
+ default:
+ case KNotifyClient::Notification:
+ KMessageBox::informationWId( winId, d->text, i18n( "Notification" ) );
+ break;
+ case KNotifyClient::Warning:
+ KMessageBox::sorryWId( winId, d->text, i18n( "Warning" ) );
+ break;
+ case KNotifyClient::Error:
+ KMessageBox::errorWId( winId, d->text, i18n( "Error" ) );
+ break;
+ case KNotifyClient::Catastrophe:
+ KMessageBox::errorWId( winId, d->text, i18n( "Fatal" ) );
+ break;
+ }
+ }
+ else
+ { //we may show the specific action button
+ int result=0;
+ QGuardedPtr<KNotification> _this=this; //this can be deleted
+ switch( d->level )
+ {
+ default:
+ case KNotifyClient::Notification:
+ result = KMessageBox::questionYesNo(d->widget, d->text, i18n( "Notification" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ case KNotifyClient::Warning:
+ result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Warning" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ case KNotifyClient::Error:
+ result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Error" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ case KNotifyClient::Catastrophe:
+ result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Fatal" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ }
+ if(result==KMessageBox::Yes && _this)
+ {
+ activate(0);
+ }
+ }
+}
+
+
+
+void KNotification::notifyByPassivePopup(const QPixmap &pix )
+{
+ QString appName = QString::fromAscii( KNotifyClient::instance()->instanceName() );
+ KIconLoader iconLoader( appName );
+ KConfig eventsFile( QString::fromAscii( KNotifyClient::instance()->instanceName()+"/eventsrc" ), true, false, "data");
+ KConfigGroup config( &eventsFile, "!Global!" );
+ QString iconName = config.readEntry( "IconName", appName );
+ QPixmap icon = iconLoader.loadIcon( iconName, KIcon::Small );
+ QString title = config.readEntry( "Comment", appName );
+ //KPassivePopup::message(title, text, icon, senderWinId);
+
+ WId winId=d->widget ? d->widget->topLevelWidget()->winId() : 0;
+
+ KPassivePopup *pop = new KPassivePopup( checkWinId(appName, winId) );
+ QObject::connect(this, SIGNAL(closed()), pop, SLOT(deleteLater()));
+
+ QVBox *vb = pop->standardView( title, pix.isNull() ? d->text: QString::null , icon );
+ QVBox *vb2=vb;
+
+ if(!pix.isNull())
+ {
+ QHBox *hb = new QHBox(vb);
+ hb->setSpacing(KDialog::spacingHint());
+ QLabel *pil=new QLabel(hb);
+ pil->setPixmap(pix);
+ pil->setScaledContents(true);
+ if(pix.height() > 80 && pix.height() > pix.width() )
+ {
+ pil->setMaximumHeight(80);
+ pil->setMaximumWidth(80*pix.width()/pix.height());
+ }
+ else if(pix.width() > 80 && pix.height() <= pix.width())
+ {
+ pil->setMaximumWidth(80);
+ pil->setMaximumHeight(80*pix.height()/pix.width());
+ }
+ vb=new QVBox(hb);
+ QLabel *msg = new QLabel( d->text, vb, "msg_label" );
+ msg->setAlignment( AlignLeft );
+ }
+
+
+ if ( !d->actions.isEmpty() )
+ {
+ QString linkCode=QString::fromLatin1("<p align=\"right\">");
+ int i=0;
+ for ( QStringList::ConstIterator it = d->actions.begin() ; it != d->actions.end(); ++it )
+ {
+ i++;
+ linkCode+=QString::fromLatin1("&nbsp;<a href=\"%1\">%2</a> ").arg( QString::number(i) , QStyleSheet::escape(*it) );
+ }
+ linkCode+=QString::fromLatin1("</p>");
+ KActiveLabel *link = new KActiveLabel(linkCode , vb );
+ //link->setAlignment( AlignRight );
+ QObject::disconnect(link, SIGNAL(linkClicked(const QString &)), link, SLOT(openLink(const QString &)));
+ QObject::connect(link, SIGNAL(linkClicked(const QString &)), this, SLOT(slotPopupLinkClicked(const QString &)));
+ QObject::connect(link, SIGNAL(linkClicked(const QString &)), pop, SLOT(hide()));
+ }
+
+ pop->setAutoDelete( true );
+ //pop->setTimeout(-1);
+
+ pop->setView( vb2 );
+ pop->show();
+
+}
+
+void KNotification::slotPopupLinkClicked(const QString &adr)
+{
+ m_linkClicked = true;
+ unsigned int action=adr.toUInt();
+ if(action==0)
+ return;
+
+ activate(action);
+
+ // since we've hidden the message (KNotification::notifyByPassivePopup(const QPixmap &pix ))
+ // we must now schedule overselves for deletion
+ close();
+}
+
+void KNotification::activate(unsigned int action)
+{
+ if(action==0)
+ emit activated();
+
+ emit activated(action);
+ deleteLater();
+}
+
+
+void KNotification::close()
+{
+ // if the user hasn't clicked the link, and if we got here, it means the dialog closed
+ // and we were ignored
+ if (!m_linkClicked)
+ {
+ emit ignored();
+ }
+
+ emit closed();
+ deleteLater();
+}
+
+
+void KNotification::raiseWidget()
+{
+ if(!d->widget)
+ return;
+
+ raiseWidget(d->widget);
+}
+
+
+void KNotification::raiseWidget(QWidget *w)
+{
+ //TODO this funciton is far from finished.
+ if(w->isTopLevel())
+ {
+ w->raise();
+ KWin::activateWindow( w->winId() );
+ }
+ else
+ {
+ QWidget *pw=w->parentWidget();
+ raiseWidget(pw);
+
+ if( QTabWidget *tab_widget=dynamic_cast<QTabWidget*>(pw))
+ {
+ tab_widget->showPage(w);
+ }
+ }
+}
+
+
+
+
+
+KNotification *KNotification::event( const QString& message , const QString& text,
+ const QPixmap& pixmap, QWidget *widget,
+ const QStringList &actions, unsigned int flags)
+{
+ /* NOTE: this function still use the KNotifyClient,
+ * in the future (KDE4) all the function of the knotifyclient will be moved there.
+ * Some code here is derived from the old KNotify deamon
+ */
+
+ int level=KNotifyClient::Default;
+ QString sound;
+ QString file;
+ QString commandline;
+
+ // get config file
+ KConfig eventsFile( QString::fromAscii( KNotifyClient::instance()->instanceName()+"/eventsrc" ), true, false, "data");
+ eventsFile.setGroup(message);
+
+ KConfig configFile( QString::fromAscii( KNotifyClient::instance()->instanceName()+".eventsrc" ), true, false);
+ configFile.setGroup(message);
+
+ int present=KNotifyClient::getPresentation(message);
+ if(present==-1)
+ present=KNotifyClient::getDefaultPresentation(message);
+ if(present==-1)
+ present=0;
+
+ // get sound file name
+ if( present & KNotifyClient::Sound ) {
+ QString theSound = configFile.readPathEntry( "soundfile" );
+ if ( theSound.isEmpty() )
+ theSound = eventsFile.readPathEntry( "default_sound" );
+ if ( !theSound.isEmpty() )
+ sound = theSound;
+ }
+
+ // get log file name
+ if( present & KNotifyClient::Logfile ) {
+ QString theFile = configFile.readPathEntry( "logfile" );
+ if ( theFile.isEmpty() )
+ theFile = eventsFile.readPathEntry( "default_logfile" );
+ if ( !theFile.isEmpty() )
+ file = theFile;
+ }
+
+ // get default event level
+ if( present & KNotifyClient::Messagebox )
+ level = eventsFile.readNumEntry( "level", 0 );
+
+ // get command line
+ if (present & KNotifyClient::Execute ) {
+ commandline = configFile.readPathEntry( "commandline" );
+ if ( commandline.isEmpty() )
+ commandline = eventsFile.readPathEntry( "default_commandline" );
+ }
+
+ return userEvent( text, pixmap, widget, actions, present , level, sound, file, commandline, flags );
+}
+
+KNotification *KNotification::userEvent( const QString& text, const QPixmap& pixmap, QWidget *widget,
+ QStringList actions,int present, int level, const QString &sound, const QString &file,
+ const QString &commandline, unsigned int flags)
+{
+
+ /* NOTE: this function still use the KNotifyClient,
+ * in the futur (KDE4) all the function of the knotifyclient will be moved there.
+ * Some code of this function fome from the old KNotify deamon
+ */
+
+
+ KNotification *notify=new KNotification(widget);
+ notify->d->widget=widget;
+ notify->d->text=text;
+ notify->d->actions=actions;
+ notify->d->level=level;
+ WId winId=widget ? widget->topLevelWidget()->winId() : 0;
+
+
+ //we will catch some event that will not be fired by the old deamon
+
+
+ //we remove presentation that has been already be played, and we fire the event in the old way
+
+
+ KNotifyClient::userEvent(winId,text,present & ~( KNotifyClient::PassivePopup|KNotifyClient::Messagebox|KNotifyClient::Execute),level,sound,file);
+
+
+ if ( present & KNotifyClient::PassivePopup )
+ {
+ notify->notifyByPassivePopup( pixmap );
+ }
+ if ( present & KNotifyClient::Messagebox )
+ {
+ QTimer::singleShot(0,notify,SLOT(notifyByMessagebox()));
+ }
+ else //not a message box (because closing the event when a message box is there is suicide)
+ if(flags & CloseOnTimeout)
+ {
+ QTimer::singleShot(6*1000, notify, SLOT(close()));
+ }
+ if ( present & KNotifyClient::Execute )
+ {
+ QString appname = QString::fromAscii( KNotifyClient::instance()->instanceName() );
+ notify->notifyByExecute(commandline, QString::null,appname,text, winId, 0 );
+ }
+
+ return notify;
+
+}
+
+
+
+/* This code is there before i find a great way to perform context-dependent notifications
+ * in a way independent of kopete.
+ * i'm in fact still using the Will's old code.
+ */
+
+
+#include "kopeteeventpresentation.h"
+#include "kopetegroup.h"
+#include "kopetenotifydataobject.h"
+#include "kopetenotifyevent.h"
+#include "kopetemetacontact.h"
+#include "kopeteuiglobal.h"
+#include <qimage.h>
+
+
+static KNotification *performCustomNotifications( QWidget *widget, Kopete::MetaContact * mc, const QString &message, bool& suppress)
+{
+ KNotification *n=0L;
+ //kdDebug( 14010 ) << k_funcinfo << endl;
+ if ( suppress )
+ return n;
+
+ // Anything, including the MC itself, may set suppress and prevent further notifications
+
+ /* This is a really ugly piece of logic now. The idea is to check for notifications
+ * first on the metacontact, then on each of its groups, until something suppresses
+ * any further notifications.
+ * So on the first run round this loop, dataObj points to the metacontact, and on subsequent
+ * iterations it points to one of the contact's groups. The metacontact pointer is maintained
+ * so that if a group has a chat notification set for this event, we can call execute() on the MC.
+ */
+
+ bool checkingMetaContact = true;
+ Kopete::NotifyDataObject * dataObj = mc;
+ do {
+ QString sound;
+ QString text;
+
+ if ( dataObj )
+ {
+ Kopete::NotifyEvent *evt = dataObj->notifyEvent( message );
+ if ( evt )
+ {
+ suppress = evt->suppressCommon();
+ int present = 0;
+ // sound
+ Kopete::EventPresentation *pres = evt->presentation( Kopete::EventPresentation::Sound );
+ if ( pres && pres->enabled() )
+ {
+ present = present | KNotifyClient::Sound;
+ sound = pres->content();
+ evt->firePresentation( Kopete::EventPresentation::Sound );
+ }
+ // message
+ if ( ( pres = evt->presentation( Kopete::EventPresentation::Message ) )
+ && pres->enabled() )
+ {
+ present = present | KNotifyClient::PassivePopup;
+ text = pres->content();
+ evt->firePresentation( Kopete::EventPresentation::Message );
+ }
+ // chat
+ if ( ( pres = evt->presentation( Kopete::EventPresentation::Chat ) )
+ && pres->enabled() )
+ {
+ mc->execute();
+ evt->firePresentation( Kopete::EventPresentation::Chat );
+ }
+ // fire the event
+ n=KNotification::userEvent( text, mc->photo(), widget, QStringList() , present, 0, sound, QString::null, QString::null , KNotification::CloseOnTimeout);
+ }
+ }
+
+ if ( mc )
+ {
+ if ( checkingMetaContact )
+ {
+ // only executed on first iteration
+ checkingMetaContact = false;
+ dataObj = mc->groups().first();
+ }
+ else
+ dataObj = mc->groups().next();
+ }
+ }
+ while ( dataObj && !suppress );
+ return n;
+}
+
+
+
+
+KNotification *KNotification::event( Kopete::MetaContact *mc, const QString& message ,
+ const QString& text, const QPixmap& pixmap, QWidget *widget,
+ const QStringList &actions, unsigned int flags)
+{
+ if (message.isEmpty()) return 0;
+
+ bool suppress = false;
+ KNotification *n=performCustomNotifications( widget, mc, message, suppress);
+
+ if ( suppress )
+ {
+ //kdDebug( 14000 ) << "suppressing common notifications" << endl;
+ return n; // custom notifications don't create a single unique id
+ }
+ else
+ {
+ //kdDebug( 14000 ) << "carrying out common notifications" << endl;
+ return event( message, text, pixmap, widget , actions, flags);
+ }
+}
+
+
+
+
+
+#include "knotification.moc"
+
+
+