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 "kopeteuiglobal.h"
42 #include "kopeteglobal.h"
43 #include "kopeteview.h"
44 #include "kopetecontact.h"
49 Kopete::ContactPtrList mContactList
;
50 const Kopete::Contact
*mUser
;
51 QMap
<const Kopete::Contact
*, Kopete::OnlineStatus
> contactStatus
;
52 Kopete::Protocol
*mProtocol
;
55 unsigned int refcount
;
56 bool customDisplayName
;
61 Kopete::MessageHandlerChain::Ptr chains
[3];
62 Kopete::ChatSession::Form form
;
65 Kopete::ChatSession::ChatSession( const Kopete::Contact
*user
,
66 Kopete::ContactPtrList others
, Kopete::Protocol
*protocol
, Kopete::ChatSession::Form form
)
67 : QObject( user
->account())
73 d
->mProtocol
= protocol
;
74 d
->isEmpty
= others
.isEmpty();
75 d
->mCanBeDeleted
= true;
78 d
->customDisplayName
= false;
82 for ( i
= 0; others
.size() != i
; i
++ )
83 addContact( others
[i
], true );
85 connect( user
, SIGNAL(contactDestroyed(Kopete::Contact
*)), this, SLOT(slotMyselfDestroyed(Kopete::Contact
*)) );
86 connect( user
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ), this,
87 SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ) );
89 if( user
->metaContact() )
90 connect( user
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
92 slotUpdateDisplayName();
95 Kopete::ChatSession::~ChatSession()
97 //for ( Kopete::Contact *c = d->mContactList.first(); c; c = d->mContactList.next() )
98 // c->setConversations( c->conversations() - 1 );
102 d
->mCanBeDeleted
= false; //prevent double deletion
103 Kopete::ChatSessionManager::self()->removeSession( this );
104 emit
closing( this );
108 void Kopete::ChatSession::slotOnlineStatusChanged( Kopete::Contact
*c
, const Kopete::OnlineStatus
&status
, const Kopete::OnlineStatus
&oldStatus
)
110 slotUpdateDisplayName();
111 emit
onlineStatusChanged((Kopete::Contact
*)c
, status
, oldStatus
);
114 void Kopete::ChatSession::setContactOnlineStatus( const Kopete::Contact
*contact
, const Kopete::OnlineStatus
&status
)
116 Kopete::OnlineStatus oldStatus
= d
->contactStatus
[ contact
];
117 d
->contactStatus
[ contact
] = status
;
118 disconnect( contact
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
119 this, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
120 emit
onlineStatusChanged( (Kopete::Contact
*)contact
, status
, oldStatus
);
123 const Kopete::OnlineStatus
Kopete::ChatSession::contactOnlineStatus( const Kopete::Contact
*contact
) const
125 if ( d
->contactStatus
.contains( contact
) )
126 return d
->contactStatus
[ contact
];
128 return contact
->onlineStatus();
131 const QString
Kopete::ChatSession::displayName()
133 if ( d
->displayName
.isNull() )
135 slotUpdateDisplayName();
138 return d
->displayName
;
141 void Kopete::ChatSession::setDisplayName( const QString
&newName
)
143 d
->displayName
= newName
;
144 d
->customDisplayName
= true;
145 emit
displayNameChanged();
148 void Kopete::ChatSession::slotUpdateDisplayName()
150 if( d
->customDisplayName
)
154 //If there is no member yet, don't try to update the display name
155 if ( d
->mContactList
.isEmpty() )
158 d
->displayName
.clear();
159 for(int i
= 0; i
!= d
->mContactList
.size(); i
++ )
161 Kopete::Contact
* c
= d
->mContactList
[i
];
162 if(! d
->displayName
.isNull() )
163 d
->displayName
.append( QString::fromLatin1( ", " ) ) ;
165 if ( c
->metaContact() )
166 d
->displayName
.append( c
->metaContact()->displayName() );
169 QString nick
=c
->property(Kopete::Global::Properties::self()->nickName()).value().toString();
170 d
->displayName
.append( nick
.isEmpty() ? c
->contactId() : nick
);
174 //If we have only 1 contact, add the status of him
175 if ( d
->mContactList
.count() == 1 )
177 d
->displayName
.append( QString::fromLatin1( " (%1)" ).arg( d
->mContactList
.first()->onlineStatus().description() ) );
180 emit
displayNameChanged();
183 const Kopete::ContactPtrList
& Kopete::ChatSession::members() const
185 return d
->mContactList
;
188 const Kopete::Contact
* Kopete::ChatSession::myself() const
193 Kopete::Protocol
* Kopete::ChatSession::protocol() const
199 #include "kopetemessagehandler.h"
200 #include "kopetemessageevent.h"
202 // FIXME: remove this and the friend decl in KMM
203 class Kopete::TemporaryKMMCallbackAppendMessageHandler
: public Kopete::MessageHandler
205 Kopete::ChatSession
*manager
;
207 TemporaryKMMCallbackAppendMessageHandler( Kopete::ChatSession
*manager
)
211 void handleMessage( Kopete::MessageEvent
*event
)
213 Kopete::Message message
= event
->message();
214 emit manager
->messageAppended( message
, manager
);
219 class TempFactory
: public Kopete::MessageHandlerFactory
222 Kopete::MessageHandler
*create( Kopete::ChatSession
*manager
, Kopete::Message::MessageDirection
)
224 return new Kopete::TemporaryKMMCallbackAppendMessageHandler( manager
);
226 int filterPosition( Kopete::ChatSession
*, Kopete::Message::MessageDirection
)
228 // FIXME: somewhere after everyone else.
233 Kopete::MessageHandlerChain::Ptr
Kopete::ChatSession::chainForDirection( Kopete::Message::MessageDirection dir
)
235 if( dir
< 0 || dir
> 2)
236 kFatal(14000) << "invalid message direction " << dir
;
237 if( !d
->chains
[dir
] )
239 TempFactory theTempFactory
;
240 d
->chains
[dir
] = Kopete::MessageHandlerChain::create( this, dir
);
242 return d
->chains
[dir
];
245 void Kopete::ChatSession::sendMessage( Kopete::Message
&message
)
247 message
.setManager( this );
248 Kopete::Message sentMessage
= message
;
249 if ( !Kopete::CommandHandler::commandHandler()->processMessage( message
, this ) )
251 emit
messageSent( sentMessage
, this );
252 if ( !account()->isAway() || Kopete::BehaviorSettings::self()->enableEventsWhileAway() )
254 KNotification::event(QString::fromLatin1( "kopete_outgoing" ), i18n( "Outgoing Message Sent" ) );
263 void Kopete::ChatSession::messageSucceeded()
265 emit
messageSuccess();
268 void Kopete::ChatSession::emitNudgeNotification()
270 KNotification::event( QString::fromLatin1("buzz_nudge"), i18n("A contact sent you a buzz/nudge.") );
273 void Kopete::ChatSession::appendMessage( Kopete::Message
&msg
)
275 msg
.setManager( this );
277 if ( msg
.direction() == Kopete::Message::Inbound
)
279 QString nick
=myself()->property(Kopete::Global::Properties::self()->nickName()).value().toString();
280 if ( Kopete::BehaviorSettings::self()->highlightEnabled() && !nick
.isEmpty() &&
281 msg
.plainBody().contains( QRegExp( QString::fromLatin1( "\\b(%1)\\b" ).arg( nick
), Qt::CaseInsensitive
) ) )
283 msg
.setImportance( Kopete::Message::Highlight
);
286 emit
messageReceived( msg
, this );
289 // outbound messages here are ones the user has sent that are now
290 // getting reflected back to the chatwindow. they should go down
291 // the incoming chain.
292 Kopete::Message::MessageDirection chainDirection
= msg
.direction();
293 if( chainDirection
== Kopete::Message::Outbound
)
294 chainDirection
= Kopete::Message::Inbound
;
296 chainForDirection( chainDirection
)->processMessage( msg
);
297 // emit messageAppended( msg, this );
300 void Kopete::ChatSession::addContact( const Kopete::Contact
*c
, const Kopete::OnlineStatus
&initialStatus
, bool suppress
)
302 if( !d
->contactStatus
.contains(c
) )
303 d
->contactStatus
[ c
] = initialStatus
;
304 addContact( c
, suppress
);
307 void Kopete::ChatSession::addContact( const Kopete::Contact
*c
, bool suppress
)
310 if ( d
->mContactList
.contains( (Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)(Kopete::Contact
*)c
) )
312 kDebug( 14010 ) << "Contact already exists";
313 emit
contactAdded( c
, suppress
);
317 if ( d
->mContactList
.count() == 1 && d
->isEmpty
)
319 kDebug( 14010 ) << " FUCKER ZONE ";
320 /* We have only 1 contact before, so the status of the
321 message manager was given from that contact status */
322 Kopete::Contact
*old
= d
->mContactList
.first();
323 d
->mContactList
.removeAll( old
);
324 d
->mContactList
.append( (Kopete::Contact
*)c
);
326 disconnect( old
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
327 this, SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
329 if ( old
->metaContact() )
331 disconnect( old
->metaContact(), SIGNAL( displayNameChanged( const QString
&, const QString
& ) ), this, SLOT( slotUpdateDisplayName() ) );
332 disconnect( old
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
335 disconnect( old
, SIGNAL( propertyChanged( Kopete::PropertyContainer
*, const QString
&, const QVariant
&, const QVariant
& ) ), this, SLOT( slotUpdateDisplayName() ) );
336 emit
contactAdded( c
, suppress
);
337 emit
contactRemoved( old
, QString() );
341 d
->mContactList
.append( (Kopete::Contact
*)c
);
342 emit
contactAdded( c
, suppress
);
345 connect( c
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
346 this, SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
348 if ( c
->metaContact() )
350 connect( c
->metaContact(), SIGNAL( displayNameChanged( const QString
&, const QString
& ) ), this, SLOT( slotUpdateDisplayName() ) );
351 connect( c
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
354 connect( c
, SIGNAL( propertyChanged( Kopete::PropertyContainer
*, const QString
&, const QVariant
&, const QVariant
& ) ), this, SLOT( slotUpdateDisplayName() ) );
355 connect( c
, SIGNAL( contactDestroyed( Kopete::Contact
* ) ), this, SLOT( slotContactDestroyed( Kopete::Contact
* ) ) );
357 slotUpdateDisplayName();
362 void Kopete::ChatSession::removeContact( const Kopete::Contact
*c
, const QString
& reason
, Qt::TextFormat format
, bool suppressNotification
)
365 if ( !c
|| !d
->mContactList
.contains( (Kopete::Contact
*)c
) )
368 if ( d
->mContactList
.count() == 1 )
370 kDebug( 14010 ) << "Contact not removed. Keep always one contact";
375 d
->mContactList
.removeAll( (Kopete::Contact
*)c
);
377 disconnect( c
, SIGNAL( onlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
& ) ),
378 this, SLOT( slotOnlineStatusChanged( Kopete::Contact
*, const Kopete::OnlineStatus
&, const Kopete::OnlineStatus
&) ) );
380 if ( c
->metaContact() )
382 disconnect( c
->metaContact(), SIGNAL( displayNameChanged( const QString
&, const QString
& ) ), this, SLOT( slotUpdateDisplayName() ) );
383 disconnect( c
->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
386 disconnect( c
, SIGNAL( propertyChanged( Kopete::PropertyContainer
*, const QString
&, const QVariant
&, const QVariant
& ) ), this, SLOT( slotUpdateDisplayName() ) );
387 disconnect( c
, SIGNAL( contactDestroyed( Kopete::Contact
* ) ), this, SLOT( slotContactDestroyed( Kopete::Contact
* ) ) );
389 slotUpdateDisplayName();
392 d
->contactStatus
.remove( c
);
394 emit
contactRemoved( c
, reason
, format
, suppressNotification
);
397 void Kopete::ChatSession::receivedTypingMsg( const Kopete::Contact
*c
, bool t
)
399 emit
remoteTyping( c
, t
);
402 void Kopete::ChatSession::receivedTypingMsg( const QString
&contactId
, bool t
)
406 // FIXME: this needs better design. We can't iterate through List to find out who got what ID
407 // hash will be better for that, right ?
408 for ( i
=0; i
!= d
->mContactList
.size(); i
++ )
410 if ( (d
->mContactList
[i
])->contactId() == contactId
)
412 receivedTypingMsg( d
->mContactList
[i
], t
);
418 void Kopete::ChatSession::typing( bool t
)
420 emit
myselfTyping( t
);
423 void Kopete::ChatSession::receivedEventNotification( const QString
& notificationText
)
425 emit
eventNotification( notificationText
);
428 void Kopete::ChatSession::setCanBeDeleted ( bool b
)
430 d
->mCanBeDeleted
= b
;
431 if (d
->refcount
< (b
?1:0) && !d
->view
)
435 void Kopete::ChatSession::ref ()
439 void Kopete::ChatSession::deref ()
442 if ( d
->refcount
< 1 && d
->mCanBeDeleted
&& !d
->view
)
446 KopeteView
* Kopete::ChatSession::view( bool canCreate
, const QString
&requestedPlugin
)
448 if ( !d
->view
&& canCreate
)
450 d
->view
= Kopete::ChatSessionManager::self()->createView( this, requestedPlugin
);
453 connect( d
->view
->mainWidget(), SIGNAL( closing( KopeteView
* ) ), this, SLOT( slotViewDestroyed( ) ) );
457 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error
,
458 i18n( "<qt>An error has occurred while creating a new chat window. The chat window has not been created.</qt>" ),
459 i18n( "Error While Creating Chat Window" ) );
465 void Kopete::ChatSession::slotViewDestroyed()
468 if ( d
->mCanBeDeleted
&& d
->refcount
< 1)
472 Kopete::Account
*Kopete::ChatSession::account() const
477 return myself()->account();
480 void Kopete::ChatSession::slotContactDestroyed( Kopete::Contact
*contact
)
482 if( !contact
|| !d
->mContactList
.contains( contact
) )
485 //This is a workaround to prevent crash if the contact get deleted.
486 // in the best case, we should ask the protocol to recreate a temporary contact.
487 // (remember: the contact may be deleted when the users removes it from the contact list, or when closing kopete )
488 d
->mContactList
.removeAll( contact
);
489 emit
contactRemoved( contact
, QString() );
491 if ( d
->mContactList
.isEmpty() )
495 void Kopete::ChatSession::slotMyselfDestroyed( Kopete::Contact
*contact
)
501 bool Kopete::ChatSession::mayInvite() const
506 void Kopete::ChatSession::inviteContact(const QString
& )
508 //default implementation do nothing
511 void Kopete::ChatSession::setMayInvite( bool b
)
516 void Kopete::ChatSession::raiseView()
518 KopeteView
*v
=view(true, Kopete::BehaviorSettings::self()->viewPlugin() );
523 Kopete::ChatSession::Form
Kopete::ChatSession::form() const
528 #include "kopetechatsession.moc"
532 // vim: set noet ts=4 sts=4 sw=4: