2 kopetechatsession.cpp - Manages all chats
4 Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
5 Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
6 Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
7 Copyright (c) 2002-2004 by Olivier Goffart <ogoffart@kde.org>
8 Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
9 Copyright (c) 2005 by Michaƫl Larouche <larouche@kde.org>
11 Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
13 *************************************************************************
15 * This library is free software; you can redistribute it and/or *
16 * modify it under the terms of the GNU Lesser General Public *
17 * License as published by the Free Software Foundation; either *
18 * version 2 of the License, or (at your option) any later version. *
20 *************************************************************************
23 #include "kopetechatsession.h"
25 #include <qapplication.h>
29 #include <kdeversion.h>
32 #include <kmessagebox.h>
33 #include <knotification.h>
35 #include "kopeteaccount.h"
36 #include "kopetebehaviorsettings.h"
37 #include "kopetecommandhandler.h"
38 #include "kopetechatsessionmanager.h"
39 #include "kopetemessagehandlerchain.h"
40 #include "kopetemetacontact.h"
41 #include "knotification.h"
42 #include "kopeteuiglobal.h"
43 #include "kopeteglobal.h"
44 #include "kopeteview.h"
45 #include "kopetecontact.h"
50 Kopete::ContactPtrList mContactList
;
51 const Kopete::Contact
*mUser
;
52 QMap
<const Kopete::Contact
*, Kopete::OnlineStatus
> contactStatus
;
53 Kopete::Protocol
*mProtocol
;
56 unsigned int refcount
;
57 bool customDisplayName
;
62 Kopete::MessageHandlerChain::Ptr chains
[3];
65 Kopete::ChatSession::ChatSession( const Kopete::Contact
*user
,
66 Kopete::ContactPtrList others
, Kopete::Protocol
*protocol
)
67 : QObject( user
->account())
73 d
->mProtocol
= protocol
;
74 d
->isEmpty
= others
.isEmpty();
75 d
->mCanBeDeleted
= true;
78 d
->customDisplayName
= false;
81 for ( i
= 0; others
.size() != i
; i
++ )
82 addContact( others
[i
], true );
84 connect( user
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ), this,
85 SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ) );
87 if( user
->metaContact() )
88 connect( user
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
90 slotUpdateDisplayName();
93 Kopete::ChatSession::~ChatSession()
95 //for ( Kopete::Contact *c = d->mContactList.first(); c; c = d->mContactList.next() )
96 // c->setConversations( c->conversations() - 1 );
100 d
->mCanBeDeleted
= false; //prevent double deletion
101 Kopete::ChatSessionManager::self()->removeSession( this );
102 emit
closing( this );
106 void Kopete::ChatSession::slotOnlineStatusChanged( Kopete::Contact
*c
, const Kopete::OnlineStatus
&status
, const Kopete::OnlineStatus
&oldStatus
)
108 slotUpdateDisplayName();
109 emit
onlineStatusChanged((Kopete::Contact
*)c
, status
, oldStatus
);
112 void Kopete::ChatSession::setContactOnlineStatus( const Kopete::Contact
*contact
, const Kopete::OnlineStatus
&status
)
114 Kopete::OnlineStatus oldStatus
= d
->contactStatus
[ contact
];
115 d
->contactStatus
[ contact
] = status
;
116 disconnect( contact
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
117 this, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
118 emit
onlineStatusChanged( (Kopete::Contact
*)contact
, status
, oldStatus
);
121 const Kopete::OnlineStatus
Kopete::ChatSession::contactOnlineStatus( const Kopete::Contact
*contact
) const
123 if ( d
->contactStatus
.contains( contact
) )
124 return d
->contactStatus
[ contact
];
126 return contact
->onlineStatus();
129 const QString
Kopete::ChatSession::displayName()
131 if ( d
->displayName
.isNull() )
133 slotUpdateDisplayName();
136 return d
->displayName
;
139 void Kopete::ChatSession::setDisplayName( const QString
&newName
)
141 d
->displayName
= newName
;
142 d
->customDisplayName
= true;
143 emit
displayNameChanged();
146 void Kopete::ChatSession::slotUpdateDisplayName()
148 if( d
->customDisplayName
)
152 //If there is no member yet, don't try to update the display name
153 if ( d
->mContactList
.isEmpty() )
156 d
->displayName
.clear();
157 for(int i
= 0; i
!= d
->mContactList
.size(); i
++ )
159 Kopete::Contact
* c
= d
->mContactList
[i
];
160 if(! d
->displayName
.isNull() )
161 d
->displayName
.append( QString::fromLatin1( ", " ) ) ;
163 if ( c
->metaContact() )
164 d
->displayName
.append( c
->metaContact()->displayName() );
167 QString nick
=c
->property(Kopete::Global::Properties::self()->nickName()).value().toString();
168 d
->displayName
.append( nick
.isEmpty() ? c
->contactId() : nick
);
172 //If we have only 1 contact, add the status of him
173 if ( d
->mContactList
.count() == 1 )
175 d
->displayName
.append( QString::fromLatin1( " (%1)" ).arg( d
->mContactList
.first()->onlineStatus().description() ) );
178 emit
displayNameChanged();
181 const Kopete::ContactPtrList
& Kopete::ChatSession::members() const
183 return d
->mContactList
;
186 const Kopete::Contact
* Kopete::ChatSession::myself() const
191 Kopete::Protocol
* Kopete::ChatSession::protocol() const
197 #include "kopetemessagehandler.h"
198 #include "kopetemessageevent.h"
200 // FIXME: remove this and the friend decl in KMM
201 class Kopete::TemporaryKMMCallbackAppendMessageHandler
: public Kopete::MessageHandler
203 Kopete::ChatSession
*manager
;
205 TemporaryKMMCallbackAppendMessageHandler( Kopete::ChatSession
*manager
)
209 void handleMessage( Kopete::MessageEvent
*event
)
211 Kopete::Message message
= event
->message();
212 emit manager
->messageAppended( message
, manager
);
217 class TempFactory
: public Kopete::MessageHandlerFactory
220 Kopete::MessageHandler
*create( Kopete::ChatSession
*manager
, Kopete::Message::MessageDirection
)
222 return new Kopete::TemporaryKMMCallbackAppendMessageHandler( manager
);
224 int filterPosition( Kopete::ChatSession
*, Kopete::Message::MessageDirection
)
226 // FIXME: somewhere after everyone else.
231 Kopete::MessageHandlerChain::Ptr
Kopete::ChatSession::chainForDirection( Kopete::Message::MessageDirection dir
)
233 if( dir
< 0 || dir
> 2)
234 kFatal(14000) << k_funcinfo
<< "invalid message direction " << dir
<< endl
;
235 if( !d
->chains
[dir
] )
237 TempFactory theTempFactory
;
238 d
->chains
[dir
] = Kopete::MessageHandlerChain::create( this, dir
);
240 return d
->chains
[dir
];
243 void Kopete::ChatSession::sendMessage( Kopete::Message
&message
)
245 message
.setManager( this );
246 Kopete::Message sentMessage
= message
;
247 if ( !Kopete::CommandHandler::commandHandler()->processMessage( message
, this ) )
249 emit
messageSent( sentMessage
, this );
250 if ( !account()->isAway() || Kopete::BehaviorSettings::self()->enableEventsWhileAway() )
252 KNotification::event(QString::fromLatin1( "kopete_outgoing" ), i18n( "Outgoing Message Sent" ) );
261 void Kopete::ChatSession::messageSucceeded()
263 emit
messageSuccess();
266 void Kopete::ChatSession::emitNudgeNotification()
268 KNotification::event( QString::fromLatin1("buzz_nudge"), i18n("A contact sent you a buzz/nudge.") );
271 void Kopete::ChatSession::appendMessage( Kopete::Message
&msg
)
273 msg
.setManager( this );
275 if ( msg
.direction() == Kopete::Message::Inbound
)
277 QString nick
=myself()->property(Kopete::Global::Properties::self()->nickName()).value().toString();
278 if ( Kopete::BehaviorSettings::self()->highlightEnabled() && !nick
.isEmpty() &&
279 msg
.plainBody().contains( QRegExp( QString::fromLatin1( "\\b(%1)\\b" ).arg( nick
), Qt::CaseInsensitive
) ) )
281 msg
.setImportance( Kopete::Message::Highlight
);
284 emit
messageReceived( msg
, this );
287 // outbound messages here are ones the user has sent that are now
288 // getting reflected back to the chatwindow. they should go down
289 // the incoming chain.
290 Kopete::Message::MessageDirection chainDirection
= msg
.direction();
291 if( chainDirection
== Kopete::Message::Outbound
)
292 chainDirection
= Kopete::Message::Inbound
;
294 chainForDirection( chainDirection
)->processMessage( msg
);
295 // emit messageAppended( msg, this );
298 void Kopete::ChatSession::addContact( const Kopete::Contact
*c
, const Kopete::OnlineStatus
&initialStatus
, bool suppress
)
300 if( !d
->contactStatus
.contains(c
) )
301 d
->contactStatus
[ c
] = initialStatus
;
302 addContact( c
, suppress
);
305 void Kopete::ChatSession::addContact( const Kopete::Contact
*c
, bool suppress
)
307 //kDebug( 14010 ) << k_funcinfo << endl;
308 if ( d
->mContactList
.contains( (Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)c
) )
310 kDebug( 14010 ) << k_funcinfo
<< "Contact already exists" <<endl
;
311 emit
contactAdded( c
, suppress
);
315 if ( d
->mContactList
.count() == 1 && d
->isEmpty
)
317 kDebug( 14010 ) << k_funcinfo
<< " FUCKER ZONE " << endl
;
318 /* We have only 1 contact before, so the status of the
319 message manager was given from that contact status */
320 Kopete::Contact
*old
= d
->mContactList
.first();
321 d
->mContactList
.removeAll( old
);
322 d
->mContactList
.append( (Kopete::Contact
*)c
);
324 disconnect( old
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
325 this, SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
327 if ( old
->metaContact() )
329 disconnect( old
->metaContact(), SIGNAL( displayNameChanged( const QString
&, const QString
& ) ), this, SLOT( slotUpdateDisplayName() ) );
330 disconnect( old
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
333 disconnect( old
, SIGNAL( propertyChanged( Kopete::Contact
*, const QString
&, const QVariant
&, const QVariant
& ) ), this, SLOT( slotUpdateDisplayName() ) );
334 emit
contactAdded( c
, suppress
);
335 emit
contactRemoved( old
, QString::null
);
339 d
->mContactList
.append( (Kopete::Contact
*)c
);
340 emit
contactAdded( c
, suppress
);
343 connect( c
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
344 this, SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
346 if ( c
->metaContact() )
348 connect( c
->metaContact(), SIGNAL( displayNameChanged( const QString
&, const QString
& ) ), this, SLOT( slotUpdateDisplayName() ) );
349 connect( c
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
352 connect( c
, SIGNAL( propertyChanged( Kopete::Contact
*, const QString
&, const QVariant
&, const QVariant
& ) ), this, SLOT( slotUpdateDisplayName() ) );
353 connect( c
, SIGNAL( contactDestroyed( Kopete::Contact
* ) ), this, SLOT( slotContactDestroyed( Kopete::Contact
* ) ) );
355 slotUpdateDisplayName();
360 void Kopete::ChatSession::removeContact( const Kopete::Contact
*c
, const QString
& reason
, Kopete::Message::MessageFormat format
, bool suppressNotification
)
362 kDebug( 14010 ) << k_funcinfo
<< endl
;
363 if ( !c
|| !d
->mContactList
.contains( (Kopete::Contact
*)c
) )
366 if ( d
->mContactList
.count() == 1 )
368 kDebug( 14010 ) << k_funcinfo
<< "Contact not removed. Keep always one contact" << endl
;
373 d
->mContactList
.removeAll( (Kopete::Contact
*)c
);
375 disconnect( c
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
376 this, SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
378 if ( c
->metaContact() )
380 disconnect( c
->metaContact(), SIGNAL( displayNameChanged( const QString
&, const QString
& ) ), this, SLOT( slotUpdateDisplayName() ) );
381 disconnect( c
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
384 disconnect( c
, SIGNAL( propertyChanged( Kopete::Contact
*, const QString
&, const QVariant
&, const QVariant
& ) ), this, SLOT( slotUpdateDisplayName() ) );
385 disconnect( c
, SIGNAL( contactDestroyed( Kopete::Contact
* ) ), this, SLOT( slotContactDestroyed( Kopete::Contact
* ) ) );
387 slotUpdateDisplayName();
390 d
->contactStatus
.remove( c
);
392 emit
contactRemoved( c
, reason
, format
, suppressNotification
);
395 void Kopete::ChatSession::receivedTypingMsg( const Kopete::Contact
*c
, bool t
)
397 emit
remoteTyping( c
, t
);
400 void Kopete::ChatSession::receivedTypingMsg( const QString
&contactId
, bool t
)
404 // FIXME: this needs better design. We can't iterate through List to find out who got what ID
405 // hash will be better for that, right ?
406 for ( i
=0; i
!= d
->mContactList
.size(); i
++ )
408 if ( (d
->mContactList
[i
])->contactId() == contactId
)
410 receivedTypingMsg( d
->mContactList
[i
], t
);
416 void Kopete::ChatSession::typing( bool t
)
418 emit
myselfTyping( t
);
421 void Kopete::ChatSession::receivedEventNotification( const QString
& notificationText
)
423 emit
eventNotification( notificationText
);
426 void Kopete::ChatSession::setCanBeDeleted ( bool b
)
428 d
->mCanBeDeleted
= b
;
429 if (d
->refcount
< (b
?1:0) && !d
->view
)
433 void Kopete::ChatSession::ref ()
437 void Kopete::ChatSession::deref ()
440 if ( d
->refcount
< 1 && d
->mCanBeDeleted
&& !d
->view
)
444 KopeteView
* Kopete::ChatSession::view( bool canCreate
, const QString
&requestedPlugin
)
446 if ( !d
->view
&& canCreate
)
448 d
->view
= Kopete::ChatSessionManager::self()->createView( this, requestedPlugin
);
451 connect( d
->view
->mainWidget(), SIGNAL( closing( KopeteView
* ) ), this, SLOT( slotViewDestroyed( ) ) );
455 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error
,
456 i18n( "<qt>An error has occurred while creating a new chat window. The chat window has not been created.</qt>" ),
457 i18n( "Error While Creating Chat Window" ) );
463 void Kopete::ChatSession::slotViewDestroyed()
466 if ( d
->mCanBeDeleted
&& d
->refcount
< 1)
470 Kopete::Account
*Kopete::ChatSession::account() const
472 return myself()->account();
475 void Kopete::ChatSession::slotContactDestroyed( Kopete::Contact
*contact
)
477 if(contact
== myself())
480 if( !contact
|| !d
->mContactList
.contains( contact
) )
483 //This is a workaround to prevent crash if the contact get deleted.
484 // in the best case, we should ask the protocol to recreate a temporary contact.
485 // (remember: the contact may be deleted when the users removes it from the contactlist, or when closing kopete )
486 d
->mContactList
.removeAll( contact
);
487 emit
contactRemoved( contact
, QString::null
);
489 if ( d
->mContactList
.isEmpty() )
493 bool Kopete::ChatSession::mayInvite() const
498 void Kopete::ChatSession::inviteContact(const QString
& )
500 //default implementation do nothing
503 void Kopete::ChatSession::setMayInvite( bool b
)
508 void Kopete::ChatSession::raiseView()
510 KopeteView
*v
=view(true, Kopete::BehaviorSettings::self()->viewPlugin() );
515 #include "kopetechatsession.moc"
519 // vim: set noet ts=4 sts=4 sw=4: