You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/libkopete/knotification.cpp

534 lines
15 KiB

/* 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., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, 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 <tqvbox.h>
#include <dcopclient.h>
#include <tqcstring.h>
#include <tqguardedptr.h>
#include <tqstylesheet.h>
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqtabwidget.h>
//TODO, make the KNotification aware of the systemtray.
#include "kopeteuiglobal.h"
static WId checkWinId( const TQString &/*appName*/, WId senderWinId )
{
if(senderWinId==0)
senderWinId=Kopete::UI::Global::sysTrayWId();
return senderWinId;
}
struct KNotification::Private
{
TQWidget *widget;
TQString text;
TQStringList actions;
int level;
};
KNotification::KNotification(TQObject *tqparent) :
TQObject(tqparent) , d(new Private)
{
m_linkClicked = false;
}
KNotification::~KNotification()
{
delete d;
}
void KNotification::notifyByExecute(const TQString &command, const TQString& event,
const TQString& fromApp, const TQString& text,
int winId, int eventId)
{
if (!command.isEmpty())
{
// kdDebug() << "executing command '" << command << "'" << endl;
TQMap<TQChar,TQString> subst;
subst.insert( 'e', event );
subst.insert( 'a', fromApp );
subst.insert( 's', text );
subst.insert( 'w', TQString::number( winId ));
subst.insert( 'i', TQString::number( eventId ));
TQString 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;
TQString action=d->actions[0];
WId winId=d->widget ? d->widget->tqtopLevelWidget()->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;
TQGuardedPtr<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(), TQString(), false );
break;
case KNotifyClient::Warning:
result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Warning" ), action, KStdGuiItem::cancel(), TQString(), false );
break;
case KNotifyClient::Error:
result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Error" ), action, KStdGuiItem::cancel(), TQString(), false );
break;
case KNotifyClient::Catastrophe:
result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Fatal" ), action, KStdGuiItem::cancel(), TQString(), false );
break;
}
if(result==KMessageBox::Yes && _this)
{
activate(0);
}
}
}
void KNotification::notifyByPassivePopup(const TQPixmap &pix )
{
TQString appName = TQString::fromAscii( KNotifyClient::instance()->instanceName() );
KIconLoader iconLoader( appName );
KConfig eventsFile( TQString::fromAscii( KNotifyClient::instance()->instanceName()+"/eventsrc" ), true, false, "data");
KConfigGroup config( &eventsFile, "!Global!" );
TQString iconName = config.readEntry( "IconName", appName );
TQPixmap icon = iconLoader.loadIcon( iconName, KIcon::Small );
TQString title = config.readEntry( "Comment", appName );
//KPassivePopup::message(title, text, icon, senderWinId);
WId winId=d->widget ? d->widget->tqtopLevelWidget()->winId() : 0;
KPassivePopup *pop = new KPassivePopup( checkWinId(appName, winId) );
TQObject::connect(this, TQT_SIGNAL(closed()), pop, TQT_SLOT(deleteLater()));
TQVBox *vb = pop->standardView( title, pix.isNull() ? d->text: TQString() , icon );
TQVBox *vb2=vb;
if(!pix.isNull())
{
TQHBox *hb = new TQHBox(vb);
hb->setSpacing(KDialog::spacingHint());
TQLabel *pil=new TQLabel(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 TQVBox(hb);
TQLabel *msg = new TQLabel( d->text, vb, "msg_label" );
msg->tqsetAlignment( AlignLeft );
}
if ( !d->actions.isEmpty() )
{
TQString linkCode=TQString::tqfromLatin1("<p align=\"right\">");
int i=0;
for ( TQStringList::ConstIterator it = d->actions.begin() ; it != d->actions.end(); ++it )
{
i++;
linkCode+=TQString::tqfromLatin1("&nbsp;<a href=\"%1\">%2</a> ").tqarg( TQString::number(i) , TQStyleSheet::escape(*it) );
}
linkCode+=TQString::tqfromLatin1("</p>");
KActiveLabel *link = new KActiveLabel(linkCode , vb );
//link->tqsetAlignment( AlignRight );
TQObject::disconnect(link, TQT_SIGNAL(linkClicked(const TQString &)), link, TQT_SLOT(openLink(const TQString &)));
TQObject::connect(link, TQT_SIGNAL(linkClicked(const TQString &)), this, TQT_SLOT(slotPopupLinkClicked(const TQString &)));
TQObject::connect(link, TQT_SIGNAL(linkClicked(const TQString &)), pop, TQT_SLOT(hide()));
}
pop->setAutoDelete( true );
//pop->setTimeout(-1);
pop->setView( vb2 );
pop->show();
}
void KNotification::slotPopupLinkClicked(const TQString &adr)
{
m_linkClicked = true;
unsigned int action=adr.toUInt();
if(action==0)
return;
activate(action);
// since we've hidden the message (KNotification::notifyByPassivePopup(const TQPixmap &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(TQWidget *w)
{
//TODO this funciton is far from finished.
if(w->isTopLevel())
{
w->raise();
KWin::activateWindow( w->winId() );
}
else
{
TQWidget *pw=w->tqparentWidget();
raiseWidget(pw);
if( TQTabWidget *tab_widget=dynamic_cast<TQTabWidget*>(pw))
{
tab_widget->showPage(w);
}
}
}
KNotification *KNotification::event( const TQString& message , const TQString& text,
const TQPixmap& pixmap, TQWidget *widget,
const TQStringList &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;
TQString sound;
TQString file;
TQString commandline;
// get config file
KConfig eventsFile( TQString::fromAscii( KNotifyClient::instance()->instanceName()+"/eventsrc" ), true, false, "data");
eventsFile.setGroup(message);
KConfig configFile( TQString::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 ) {
TQString 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 ) {
TQString 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 TQString& text, const TQPixmap& pixmap, TQWidget *widget,
TQStringList actions,int present, int level, const TQString &sound, const TQString &file,
const TQString &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(TQT_TQOBJECT(widget));
notify->d->widget=widget;
notify->d->text=text;
notify->d->actions=actions;
notify->d->level=level;
WId winId=widget ? widget->tqtopLevelWidget()->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 )
{
TQTimer::singleShot(0,notify,TQT_SLOT(notifyByMessagebox()));
}
else //not a message box (because closing the event when a message box is there is suicide)
if(flags & CloseOnTimeout)
{
TQTimer::singleShot(6*1000, notify, TQT_SLOT(close()));
}
if ( present & KNotifyClient::Execute )
{
TQString appname = TQString::fromAscii( KNotifyClient::instance()->instanceName() );
notify->notifyByExecute(commandline, TQString(),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 <tqimage.h>
static KNotification *performCustomNotifications( TQWidget *widget, Kopete::MetaContact * mc, const TQString &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 {
TQString sound;
TQString 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, TQStringList() , present, 0, sound, TQString(), TQString() , 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 TQString& message ,
const TQString& text, const TQPixmap& pixmap, TQWidget *widget,
const TQStringList &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"