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.
534 lines
15 KiB
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 *parent) :
|
|
TQObject(parent) , 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(" <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->parentWidget();
|
|
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"
|
|
|
|
|
|
|