1754 lines
55 KiB
C++
1754 lines
55 KiB
C++
/***************************************************************************
|
|
chat.cpp - description
|
|
-------------------
|
|
begin : Wed Jan 15 22:41:32 CST 2003
|
|
copyright : (C) 2003 by Mike K. Bennett
|
|
email : mkb137b@hotmail.com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "chat.h"
|
|
|
|
|
|
#include "../contact/contactextension.h"
|
|
#include "../contact/invitedcontact.h"
|
|
#include "../contact/msnobject.h"
|
|
#include "../model/contactlist.h"
|
|
#include "../utils/kmessconfig.h"
|
|
#include "../utils/kmessshared.h"
|
|
#include "../currentaccount.h"
|
|
#include "../kmessapplication.h"
|
|
#include "../kmessdebug.h"
|
|
#include "../kmess.h"
|
|
#include "chathistorymanager.h"
|
|
#include "chathistorywriter.h"
|
|
#include "chatmessageview.h"
|
|
#include "chatwindow.h"
|
|
#include "contactswidget.h"
|
|
|
|
#include <QDir>
|
|
#include <QElapsedTimer>
|
|
#include <QFile>
|
|
#include <QRegExp>
|
|
#include <QUrl>
|
|
|
|
#include <QFileDialog>
|
|
#include <KLocalizedString>
|
|
#include <KMessageBox>
|
|
|
|
|
|
|
|
#ifdef KMESSDEBUG_CHAT
|
|
#define KMESSDEBUG_CHAT_GENERAL
|
|
#define KMESSDEBUG_CHAT_CONTACTS
|
|
#define KMESSDEBUG_CHAT_FILETRANSFER
|
|
// #define KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
#endif
|
|
|
|
|
|
|
|
// The constructor
|
|
Chat::Chat( QWidget *parent )
|
|
: ChatView( parent )
|
|
, firstMessage_( true )
|
|
, initialized_( false )
|
|
, logWriter_( 0 )
|
|
, msnSwitchboardConnection_( 0 )
|
|
{
|
|
hide();
|
|
|
|
// Setup the contact typing information timer
|
|
typingTimer_.setSingleShot( true );
|
|
|
|
connect( &typingTimer_, SIGNAL( timeout() ),
|
|
this, SLOT ( contactTyping() ) );
|
|
}
|
|
|
|
|
|
|
|
// The destructor
|
|
Chat::~Chat()
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "DESTROYED.";
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
// Choose the contact to start an invitation with.
|
|
const QString Chat::chooseContact()
|
|
{
|
|
if( KMESS_NULL(msnSwitchboardConnection_) ) return QString();
|
|
|
|
// Choose the contact.
|
|
const QStringList &contactsInChat = msnSwitchboardConnection_->getContactsInChat();
|
|
switch( contactsInChat.count() )
|
|
{
|
|
case 0:
|
|
// No contacts in the chat
|
|
if( msnSwitchboardConnection_->getLastContact().isEmpty() )
|
|
{
|
|
// Send invitation to the first contact that should appear in the chat.
|
|
return msnSwitchboardConnection_->getFirstContact();
|
|
}
|
|
else
|
|
{
|
|
// Resume chat with last contact
|
|
return msnSwitchboardConnection_->getLastContact();
|
|
}
|
|
|
|
case 1:
|
|
// Choose the only contact available in the chat
|
|
return contactsInChat.first();
|
|
|
|
default:
|
|
// Multiple contacts in the chat.
|
|
// TODO: The official client opens a contact-choose dialog here, and starts a new chat with the selected contact.
|
|
KMessageBox::error( this, i18nc( "Error dialog box text",
|
|
"You cannot send invitations when there are multiple contacts in a chat. "
|
|
"Please start a separate chat with the contact you wanted to send the invitation to." ) );
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// A contact joined the chat
|
|
void Chat::contactJoined( ContactBase* contact, bool offlineContact )
|
|
{
|
|
// Don't replace the bool offlineContact with contact->isOffline(), because there may be
|
|
// real chats with invisible contacts
|
|
const QString& handle = contact->getHandle();
|
|
const QString& friendlyName = contact->getFriendlyName( STRING_CLEANED_ESCAPED );
|
|
const QStringList participants ( getParticipants() );
|
|
|
|
// Don't show the message when the contact went/is offline (because in such cases, the message
|
|
// is unreliable, and if we started the chat we're just pretending the contact joined)
|
|
if( ! offlineContact && ( currentAccount_->getShowSessionInfo() || participants.count() > 1 ) )
|
|
{
|
|
showMessage( ChatMessage( ChatMessage::TYPE_PRESENCE,
|
|
ChatMessage::CONTENT_PRESENCE_JOIN,
|
|
false,
|
|
i18n("%1 has joined the chat.", friendlyName ),
|
|
handle,
|
|
friendlyName ) );
|
|
}
|
|
|
|
// Change the chat title
|
|
emit chatInfoChanged();
|
|
|
|
// Allow future changes in the nickname and status to update the chat informations
|
|
if( contact )
|
|
{
|
|
// disconnect any old signals, just to be sure
|
|
disconnect( contact, 0, this, 0 );
|
|
|
|
connect( contact, SIGNAL( changedStatus() ),
|
|
this, SLOT ( slotContactChangedStatus() ) );
|
|
connect( contact, SIGNAL( changedStatus() ),
|
|
this, SIGNAL( chatInfoChanged() ) );
|
|
connect( contact, SIGNAL( changedFriendlyName() ),
|
|
this, SIGNAL( chatInfoChanged() ) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// A contact left the chat
|
|
void Chat::contactLeft( ContactBase *contact, bool isChatIdle )
|
|
{
|
|
const QString& handle = contact->getHandle();
|
|
const QStringList participants ( getParticipants() );
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "contact '" << handle << "' has left" << (isChatIdle ? " due to inactivity" : "") << ".";
|
|
#endif
|
|
|
|
// A contact has left the chat.
|
|
// Group chats: it gets disconnected completely from it
|
|
// 1-on-1 chats: the chat UI will still show the contact, so don't disconnect its update signals
|
|
if( participants.count() > 1 )
|
|
{
|
|
disconnect( contact, 0, this, 0 );
|
|
}
|
|
|
|
// Always show a message when this is a group chat,
|
|
// but never if it would be the first message.
|
|
if( ! firstMessage_ && ( currentAccount_->getShowSessionInfo() || participants.count() > 1 ) )
|
|
{
|
|
const QString& contactName = contact->getFriendlyName( STRING_CLEANED_ESCAPED );
|
|
QString message;
|
|
|
|
if( isChatIdle )
|
|
{
|
|
message = i18nc( "Message shown in chat, %1 is the contact's friendly name",
|
|
"The chat went idle, %1 has left it.",
|
|
contactName );
|
|
}
|
|
else
|
|
{
|
|
message = i18nc( "Message shown in chat, %1 is the contact's friendly name",
|
|
"%1 has left the chat.",
|
|
contactName );
|
|
}
|
|
|
|
showMessage( ChatMessage( ChatMessage::TYPE_PRESENCE,
|
|
ChatMessage::CONTENT_PRESENCE_LEAVE,
|
|
false,
|
|
message,
|
|
handle,
|
|
contactName ) );
|
|
}
|
|
|
|
// Change the caption to remove the contact who has left
|
|
emit chatInfoChanged();
|
|
}
|
|
|
|
|
|
|
|
// A contact is typing
|
|
// If called without arguments, it will only update the list of typing contacts.
|
|
void Chat::contactTyping( ContactBase *contact, bool forceExpiration )
|
|
{
|
|
int secsTo;
|
|
int nextUpdate = CHAT_TYPING_EXPIRATION_TIME; // Allow a maximum number of seconds between each update
|
|
QString handle;
|
|
QString friendlyName;
|
|
|
|
if( contact != 0 )
|
|
{
|
|
handle = contact->getHandle();
|
|
friendlyName = contact->getFriendlyName( STRING_CLEANED );
|
|
|
|
if( friendlyName.isEmpty() ) // If the contact has no friendlyName, use its handle as name
|
|
{
|
|
friendlyName = handle;
|
|
}
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "Called with forceExpiration=" << forceExpiration << ", handle=" << handle << ", friendlyName=" << friendlyName;
|
|
kmDebug() << "Current list:" << typingContactsNames_.keys();
|
|
#endif
|
|
|
|
// Do not add anything to the list if the friendly name is empty
|
|
if( ! handle.isEmpty() )
|
|
{
|
|
QElapsedTimer startTime;
|
|
|
|
// If a contact is still typing, re-insert it in the list, so that the typing start time is updated
|
|
if( typingContactsNames_.contains( handle ) )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "Removing" << handle << "from the list.";
|
|
#endif
|
|
typingContactsNames_.remove( handle );
|
|
typingContactsTimes_.remove( handle );
|
|
}
|
|
|
|
// When we receive a message from this contact, we only remove it from the typing contacts
|
|
if( ! forceExpiration )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "Adding" << handle << "to the list.";
|
|
#endif
|
|
// Add this contact to the list of contacts which are typing, and record when it has started writing
|
|
startTime.start();
|
|
typingContactsNames_.insert( handle, friendlyName );
|
|
typingContactsTimes_.insert( handle, startTime );
|
|
}
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
else
|
|
{
|
|
kmDebug() << "Removal from the list was forced.";
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Update the list to remove expired typing events
|
|
QMutableHashIterator<QString,QElapsedTimer> it( typingContactsTimes_ );
|
|
while( it.hasNext() )
|
|
{
|
|
it.next();
|
|
secsTo = (int) ( it.value().elapsed() * .001 ); // Only keep the seconds
|
|
|
|
// If the typing event has expired, stop displaying it
|
|
if( secsTo >= CHAT_TYPING_EXPIRATION_TIME )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "Removing" << handle << "from the list due to expiration.";
|
|
#endif
|
|
typingContactsNames_.remove( it.key() );
|
|
typingContactsTimes_.remove( it.key() );
|
|
}
|
|
|
|
// Find the typing event which will expire first, so we can next update when it expires
|
|
if( secsTo && secsTo < nextUpdate )
|
|
{
|
|
nextUpdate = secsTo;
|
|
}
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "List after cleanup and update:" << typingContactsNames_.keys();
|
|
#endif
|
|
|
|
// The more time passes from the last update, the more we get close to the next update
|
|
if( nextUpdate < CHAT_TYPING_EXPIRATION_TIME )
|
|
{
|
|
nextUpdate = CHAT_TYPING_EXPIRATION_TIME - nextUpdate;
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "Next update in" << nextUpdate << "seconds.";
|
|
#endif
|
|
|
|
// Update the user interface
|
|
if( typingContactsNames_.contains( handle ) )
|
|
{
|
|
contactsWidget_->contactTyping( contact );
|
|
}
|
|
else
|
|
{
|
|
contactsWidget_->messageReceived( handle );
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_TYPING_MESSAGES
|
|
kmDebug() << "emitting signal from:" << this;
|
|
#endif
|
|
|
|
// Let the other window components update, too
|
|
emit gotTypingMessage( this );
|
|
|
|
// Activate the timer for the next update if needed
|
|
if( ! typingContactsTimes_.isEmpty() )
|
|
{
|
|
typingTimer_.start( nextUpdate * 1000 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Return a list of the contacts in the chat to be used as window caption
|
|
const QString Chat::getCaption()
|
|
{
|
|
// Grep the current participants or the last participant
|
|
const QStringList participants( getParticipants() );
|
|
QString caption;
|
|
|
|
// How many people are there in here?
|
|
switch( participants.count() )
|
|
{
|
|
// The chat is completely empty - it is not even a chat!
|
|
case 0:
|
|
break;
|
|
|
|
// One contact is connected; or the only contact of the session has left
|
|
case 1:
|
|
caption = currentAccount_->getContactFriendlyNameByHandle( participants[0], STRING_CLEANED );
|
|
break;
|
|
|
|
// Two contacts are connected
|
|
case 2:
|
|
caption = i18nc( "Name of a chat tab", "%1 and %2",
|
|
currentAccount_->getContactFriendlyNameByHandle( participants[0], STRING_CLEANED ),
|
|
currentAccount_->getContactFriendlyNameByHandle( participants[1], STRING_CLEANED )
|
|
);
|
|
break;
|
|
|
|
// Three or more contacts are connected
|
|
default:
|
|
caption = i18nc( "Name of a chat tab", "%1 et al.",
|
|
currentAccount_->getContactFriendlyNameByHandle( participants[0], STRING_CLEANED )
|
|
);
|
|
}
|
|
|
|
// Replace the 'return chars' with 'space chars'
|
|
return caption.replace( "\n", " " );
|
|
}
|
|
|
|
|
|
|
|
// Return the chat window which currently contains this Chat
|
|
ChatWindow *Chat::getChatWindow()
|
|
{
|
|
return qobject_cast<ChatWindow*>( window() );
|
|
}
|
|
|
|
|
|
|
|
// Return the list of contacts in the chat
|
|
const QStringList Chat::getParticipants() const
|
|
{
|
|
if( msnSwitchboardConnection_ == 0 )
|
|
{
|
|
return lastKnownContacts_;
|
|
}
|
|
|
|
return msnSwitchboardConnection_->getContactsInChat();
|
|
}
|
|
|
|
|
|
|
|
// Return the icon to use in the tab icon chat
|
|
QIcon Chat::getParticipantsTabIcon()
|
|
{
|
|
const QStringList participants( getParticipants() );
|
|
|
|
// If there are more participants use group icon
|
|
if( participants.count() != 1 )
|
|
{
|
|
return QIcon::fromTheme( "system-users" );
|
|
}
|
|
|
|
// Else use the status icon of the current contact
|
|
const QString& handle = participants.first();
|
|
|
|
// Find the contact, if it isn't in the contact list, use the "unknown" tab icon
|
|
const ContactBase *contact = currentAccount_->getContactByHandle( handle );
|
|
if( contact == 0 )
|
|
{
|
|
return QIcon::fromTheme( "view-media-artist" );
|
|
}
|
|
|
|
return QIcon( MsnStatus::getIcon( contact->getStatus() ) );
|
|
}
|
|
|
|
|
|
|
|
// Return the list of previously sent messages
|
|
QStringList& Chat::getQuickRetypeList()
|
|
{
|
|
return quickRetypeList_;
|
|
}
|
|
|
|
|
|
|
|
// Return the list of contacts which are typing
|
|
const QStringList Chat::getTypingContacts() const
|
|
{
|
|
return typingContactsNames_.values();
|
|
}
|
|
|
|
|
|
|
|
// Return the date and time the chat has started
|
|
QDateTime Chat::getStartTime() const
|
|
{
|
|
return QDateTime( startDate_, startTime_ );
|
|
}
|
|
|
|
|
|
|
|
// Return the switchboard connection used by the chat window.
|
|
MsnSwitchboardConnection * Chat::getSwitchboardConnection() const
|
|
{
|
|
return msnSwitchboardConnection_;
|
|
}
|
|
|
|
|
|
|
|
// Initialize the object
|
|
bool Chat::initialize( MsnSwitchboardConnection *switchboardConnection )
|
|
{
|
|
if ( initialized_ )
|
|
{
|
|
kmDebug() << "already initialized.";
|
|
return false;
|
|
}
|
|
|
|
if( ! ChatView::initialize() )
|
|
{
|
|
kmDebug() << "Couldn't setup the chat view widget.";
|
|
return false;
|
|
}
|
|
|
|
// Connect the contacts widget's signals
|
|
const ContactList *contactList = currentAccount_->getContactList();
|
|
connect( contactsWidget_, SIGNAL( contactAllowed(QString) ),
|
|
this, SIGNAL( contactAllowed(QString) ) );
|
|
connect( contactsWidget_, SIGNAL( contactAdded(QString,bool) ),
|
|
this, SIGNAL( contactAdded(QString,bool) ) );
|
|
connect( contactsWidget_, SIGNAL( contactBlocked(QString,bool) ),
|
|
this, SIGNAL( contactBlocked(QString,bool) ) );
|
|
connect( contactsWidget_, SIGNAL( startPrivateChat( const QString ) ),
|
|
this, SIGNAL( startPrivateChat( const QString ) ) );
|
|
connect( contactList, SIGNAL( contactAdded(Contact*) ),
|
|
contactsWidget_, SLOT ( contactAdded(Contact*) ) );
|
|
connect( contactList, SIGNAL( contactRemoved(Contact*) ),
|
|
contactsWidget_, SLOT ( contactRemoved(Contact*) ) );
|
|
|
|
// Save the switchboard connection
|
|
setSwitchboardConnection( switchboardConnection );
|
|
|
|
// Save the date/time the chat started.
|
|
// Use it to save a chatlog later.
|
|
startDate_ = QDate::currentDate();
|
|
startTime_ = QTime::currentTime();
|
|
|
|
// Update the chat window caption with the contact name
|
|
emit chatInfoChanged();
|
|
|
|
initialized_ = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// Invite a contact to the chat
|
|
void Chat::inviteContacts( const QStringList &contacts )
|
|
{
|
|
if( msnSwitchboardConnection_ == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Invite each selected contact, if it's not in chat already
|
|
foreach( const QString &handle, contacts )
|
|
{
|
|
if( ! isContactInChat( handle, false ) )
|
|
{
|
|
msnSwitchboardConnection_->inviteContact( handle );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Return whether or not the chat is at its first incoming message
|
|
bool Chat::isChatFirstMessage()
|
|
{
|
|
return firstMessage_;
|
|
}
|
|
|
|
|
|
|
|
// Return whether or not the contact is in this chat.
|
|
bool Chat::isContactInChat( const QString &handle, bool isExclusiveChatWithContact )
|
|
{
|
|
const QStringList participants( getParticipants() );
|
|
|
|
#ifdef KMESSDEBUG_CHAT_CONTACTS
|
|
kmDebug() << "Is" << handle << "in chat" << this << "?"
|
|
<< "1-on-1 required?" << isExclusiveChatWithContact
|
|
<< "Participants:" << participants
|
|
<< "Has switchboard?" << ( msnSwitchboardConnection_ != 0 )
|
|
<< "Last known contacts:" << lastKnownContacts_;
|
|
#endif
|
|
|
|
if( participants.isEmpty() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if the contact is one of the participants to this chat.
|
|
bool check = participants.contains( handle );
|
|
|
|
// Check if is exclusive chat with the contact passed as the argument
|
|
if( check && isExclusiveChatWithContact )
|
|
{
|
|
check = ( participants.count() == 1 );
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_CONTACTS
|
|
kmDebug() << "Returning" << check;
|
|
#endif
|
|
|
|
return check;
|
|
}
|
|
|
|
|
|
|
|
// The chat is closing
|
|
void Chat::queryClose()
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Saving chat log";
|
|
#endif
|
|
|
|
// Hide it now, to increase responsiveness
|
|
hide();
|
|
|
|
// Notify ChatMaster
|
|
emit closing( this );
|
|
|
|
// Save the chat logs
|
|
endChatLogging();
|
|
|
|
/**
|
|
* TODO: Uncomment this part: it needs a _reliable_ method to check whether there are running file
|
|
* transfers. The commented part uses "ApplicationList::isEmpty()" which I've removed, the new
|
|
* method to check will probably be elsewhere. The main code of that method was this:
|
|
* <code>
|
|
// check if there are active applications
|
|
if ( mimeApplications_.count() +
|
|
p2pApplications_.count() -
|
|
abortingApplications_.count() == 0)
|
|
return true; else return false;
|
|
* </code>
|
|
*/
|
|
/*
|
|
// If there are active applications show a warning to the user
|
|
if( participants_.count() == 1 ) // Transfers are not possibile in multiple chats
|
|
{
|
|
// Check if there are active applications
|
|
ContactBase *contact = currentAccount_->getContactByHandle( participants_.first() );
|
|
if( contact != 0 && contact->getApplicationList() != 0
|
|
&& ! contact->getApplicationList()->isEmpty() )
|
|
{
|
|
int choice = KMessageBox::warningContinueCancel( this, i18n("Continue closing the chat window?\nActive transfers will be aborted!"));
|
|
if( choice == KMessageBox::Cancel )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
// The application is exiting
|
|
void Chat::queryExit()
|
|
{
|
|
// Hide it now, to increase responsiveness
|
|
hide();
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Saving chat log";
|
|
#endif
|
|
|
|
// Just save the chat before going down
|
|
endChatLogging();
|
|
}
|
|
|
|
|
|
|
|
// A message was received from a contact.
|
|
void Chat::receivedMessage(const ChatMessage &message)
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Displaying message, internal type=" << message.getType();
|
|
kmDebug() << "First message = " << firstMessage_ << ".";
|
|
#endif
|
|
|
|
showMessage( message );
|
|
|
|
// Do not notify about presence messages
|
|
if( message.getType() == ChatMessage::TYPE_PRESENCE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Update the date of the last received message. Only allowed for contacts on the contact list.
|
|
Contact *sourceContact = currentAccount_->getContactList()->getContactByHandle( message.getContactHandle() );
|
|
if( sourceContact != 0 && sourceContact->getExtension() )
|
|
{
|
|
sourceContact->getExtension()->setLastMessageDate();
|
|
}
|
|
|
|
// Check if we should send an automatic reply to the chat message
|
|
if( currentAccount_ != 0
|
|
&& msnSwitchboardConnection_ != 0
|
|
&& currentAccount_->getAutoreply()
|
|
&& ( ! lastSentAutoMessage_.isValid() || lastSentAutoMessage_.elapsed() > 120000 ) )
|
|
{
|
|
// Send an auto away message every two minutes (if the contact keeps writing)
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Sending auto reply to chat message.";
|
|
#endif
|
|
|
|
QString awayMessage( i18nc( "Automatic reply message",
|
|
"%1 (This message was sent automatically)",
|
|
currentAccount_->getAutoreplyMessage() ) );
|
|
|
|
// Send the autoreply message to the contact(s)
|
|
msnSwitchboardConnection_->sendChatMessage( awayMessage );
|
|
|
|
// Show the autoreply message
|
|
ChatMessage autoMessage = ChatMessage( ChatMessage::TYPE_OUTGOING,
|
|
ChatMessage::CONTENT_MESSAGE,
|
|
false,
|
|
awayMessage,
|
|
currentAccount_->getHandle(),
|
|
currentAccount_->getFriendlyName( STRING_ORIGINAL ),
|
|
currentAccount_->getPicturePath(),
|
|
currentAccount_->getFont(),
|
|
currentAccount_->getFontColor() );
|
|
showMessage( autoMessage );
|
|
|
|
// Save the time the message was sent, to display the next after some time
|
|
lastSentAutoMessage_.start();
|
|
}
|
|
|
|
// Remove this contact from the list of typing messages and let the parent window to update
|
|
// this chat's typing information
|
|
contactTyping( sourceContact, true );
|
|
|
|
// Emit the new message signal so that the user will receive a notification
|
|
emit gotChatMessage( message, this );
|
|
|
|
// If this was the first received message, clear the flag
|
|
if( firstMessage_ )
|
|
{
|
|
firstMessage_ = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* End the chat log.
|
|
*/
|
|
void Chat::endChatLogging()
|
|
{
|
|
kmDebug() << "Ending chat logging";
|
|
|
|
// End the log writing session, if any
|
|
delete logWriter_;
|
|
logWriter_ = 0;
|
|
|
|
if( ! currentAccount_->getSaveChatsToFile() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "User has disabled exporting chats to file. Canceling.";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
// Check if the message area is empty, we won't save empty chats
|
|
if( isEmpty() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Message area is empty, not exporting chat.";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
KMessApplication *kmessApp = static_cast<KMessApplication *>(QApplication::instance());
|
|
bool canDoUserInteraction = ! kmessApp->quitSelected(); // Not if we're exiting
|
|
|
|
QDir dir;
|
|
QString fileName;
|
|
QString extension;
|
|
Account::ChatExportFormat format;
|
|
|
|
// Set the base file name, same for all logs
|
|
const QStringList participants( getParticipants() );
|
|
if( participants.isEmpty() )
|
|
{
|
|
// Should never, never, ever happen.
|
|
fileName = "KMess";
|
|
}
|
|
else
|
|
{
|
|
fileName = participants.first();
|
|
}
|
|
|
|
dir.setPath( currentAccount_->getSaveChatPath() );
|
|
format = currentAccount_->getSaveChatsFormat();
|
|
switch( format )
|
|
{
|
|
case Account::EXPORT_TEXT:
|
|
extension = "txt";
|
|
break;
|
|
case Account::EXPORT_HTML:
|
|
extension = "html";
|
|
break;
|
|
default:
|
|
kmWarning() << "Extension is unknown for save format" << format << "! Using \"html\".";
|
|
extension = "html";
|
|
format = Account::EXPORT_HTML;
|
|
break;
|
|
}
|
|
|
|
const QString year ( startDate_.toString( "yyyy" ) );
|
|
const QString month( startDate_.toString( "MM" ) );
|
|
const QString day ( startDate_.toString( "dd" ) );
|
|
|
|
// Determine the path where to save the log file
|
|
switch( currentAccount_->getSavedChatDirectoryStructure() )
|
|
{
|
|
// Save as <logs-path>/yyyy/contactemail.ext
|
|
case Account::BYYEAR:
|
|
dir.setPath( dir.absolutePath() + "/" + year );
|
|
break;
|
|
|
|
// Save as <logs-path>/yyyy/mm/contactemail.ext
|
|
case Account::BYMONTH:
|
|
dir.setPath( dir.absolutePath() + "/" + year + "/" + month );
|
|
break;
|
|
|
|
// Save as <logs-path>/yyyy/mm/dd/contactemail.ext
|
|
case Account::BYDAY:
|
|
dir.setPath( dir.absolutePath() + "/" + year + "/" + month + "/" + day );
|
|
break;
|
|
|
|
// Save as <logs-path>/contactemail.ext
|
|
case Account::SINGLEDIRECTORY:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Try creating the directory if it doesn't exist
|
|
if( ! dir.exists() )
|
|
{
|
|
dir.mkpath( dir.absolutePath() );
|
|
}
|
|
if( ! dir.exists() ) // It couldn't be created
|
|
{
|
|
// Always warn via debug log, and also via a messagebox if we're not exiting
|
|
kmWarning() << "Unable to save the chat log. The base directory" << dir.absolutePath() << "doesn't exist and could not be created.";
|
|
|
|
if( canDoUserInteraction )
|
|
{
|
|
KMessageBox::error( getChatWindow(),
|
|
i18n( "<html>KMess could not save the log for the chat with "%1":<br />"
|
|
"The chat logs directory, "%2", does not exist.</html>",
|
|
currentAccount_->getContactFriendlyNameByHandle( participants.first() ),
|
|
dir.absolutePath() ) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Obtain the file path to be used to export the chat
|
|
QFile logFile;
|
|
QFileInfo fileInfo;
|
|
QString lastFile;
|
|
QString nextFile( KMessShared::nextSequentialFile( dir.absolutePath(), fileName, extension, lastFile ) );
|
|
|
|
fileInfo.setFile( lastFile );
|
|
|
|
// Where there already is a log file for this contact, verify if we can
|
|
// append to it. Log files should not be too big
|
|
if( lastFile.isEmpty() || ! fileInfo.isWritable() || fileInfo.size() > MAX_LOG_FILE_SIZE )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
if( lastFile.isEmpty() )
|
|
{
|
|
kmDebug() << "No file to append to, creating a new file:" << nextFile;
|
|
}
|
|
else
|
|
{
|
|
kmDebug() << "Last file" << lastFile << "cannot be used, creating a new file:" << nextFile;
|
|
}
|
|
#endif
|
|
fileInfo.setFile( nextFile );
|
|
}
|
|
else
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Appending to last chat log file:" << lastFile;
|
|
#endif
|
|
}
|
|
|
|
// Save the chat in the format requested by the user
|
|
if( ! saveChatToFile( fileInfo.absoluteFilePath(),
|
|
format,
|
|
false, /* Don't overwrite */
|
|
canDoUserInteraction /* No user interaction */ ) )
|
|
{
|
|
kmWarning() << "Exporting chat log to file" << fileInfo.absoluteFilePath() << "failed!";
|
|
|
|
// Try again with a new file if we were trying to append to the last one
|
|
if( fileInfo.absoluteFilePath() == lastFile )
|
|
{
|
|
fileInfo.setFile( nextFile );
|
|
if( ! saveChatToFile( fileInfo.absoluteFilePath(),
|
|
format,
|
|
false, /* Don't overwrite */
|
|
canDoUserInteraction /* No user interaction */ ) )
|
|
{
|
|
kmWarning() << "Second attempt at exporting chat log to file" << fileInfo.absoluteFilePath() << "failed!";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Send a message to the current chat
|
|
void Chat::sendChatMessage( const QString &message )
|
|
{
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( msnSwitchboardConnection_ );
|
|
#endif
|
|
|
|
// If we send the first message of the chat, clear the first message status
|
|
if( firstMessage_ )
|
|
{
|
|
firstMessage_ = false;
|
|
}
|
|
|
|
msnSwitchboardConnection_->sendChatMessage( message );
|
|
}
|
|
|
|
|
|
|
|
// Send an ink drawing to the current chat
|
|
void Chat::sendInkMessage( InkFormat format, const QByteArray &inkData )
|
|
{
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( msnSwitchboardConnection_ );
|
|
#endif
|
|
|
|
msnSwitchboardConnection_->sendInk( format, inkData );
|
|
}
|
|
|
|
|
|
|
|
// Reimplemented from ChatView to support logging
|
|
void Chat::showMessage( const ChatMessage& message )
|
|
{
|
|
// Log the message
|
|
if( currentAccount_->getSaveChats() && logWriter_ )
|
|
{
|
|
logWriter_->logMessage( message );
|
|
}
|
|
|
|
ChatView::showMessage( message );
|
|
}
|
|
|
|
|
|
|
|
// Send an "user is typing" message to the contacts
|
|
void Chat::sendTypingMessage()
|
|
{
|
|
if( KMESS_NULL(msnSwitchboardConnection_) ) return;
|
|
|
|
msnSwitchboardConnection_->sendTypingMessage();
|
|
}
|
|
|
|
|
|
|
|
// Send an wink to the current chat
|
|
void Chat::sendWink( const MsnObject &msnObject )
|
|
{
|
|
QString winkHtml;
|
|
msnSwitchboardConnection_->sendWink( msnObject );
|
|
|
|
// Only show the sent wink if they're enabled
|
|
if( currentAccount_->getShowWinks() )
|
|
{
|
|
WinksWidget::getHtmlFromWink( msnObject.getLocation(), winkHtml );
|
|
}
|
|
|
|
// If the stored wink didn't have a name, don't show it:
|
|
QString notificationText;
|
|
if( msnObject.getFriendly().isEmpty() )
|
|
{
|
|
notificationText = i18nc( "Message shown in the chat window (when the wink name is unknown)",
|
|
"You have sent a wink!" );
|
|
}
|
|
else
|
|
{
|
|
notificationText = i18nc( "Message shown in the chat window, %1 is the wink name",
|
|
"You have sent the "%1" wink!",
|
|
msnObject.getFriendly() );
|
|
}
|
|
|
|
ChatMessage message( ChatMessage::TYPE_NOTIFICATION,
|
|
ChatMessage::CONTENT_NOTIFICATION_WINK,
|
|
false,
|
|
notificationText + winkHtml, // Add the actual wink after the message
|
|
QString() );
|
|
showMessage( message );
|
|
|
|
// Log the message
|
|
if( currentAccount_->getSaveChats() && logWriter_ )
|
|
{
|
|
logWriter_->logMessage( message );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// The user wants to block or unblock a contact.
|
|
void Chat::setContactBlocked( QString handle, bool isBlocked )
|
|
{
|
|
emit contactBlocked( handle, isBlocked );
|
|
}
|
|
|
|
|
|
|
|
// Enable or disable the parts of the window which allow user interaction
|
|
void Chat::setEnabled( bool isEnabled )
|
|
{
|
|
// Also remove the switchboard connection when disabling
|
|
if( ! isEnabled && msnSwitchboardConnection_ != 0 )
|
|
{
|
|
setSwitchboardConnection( 0 );
|
|
}
|
|
|
|
if( ! isEnabled )
|
|
{
|
|
getChatWindow()->showStatusMessage( ChatStatusBar::Disconnected, i18n("The chat has been disabled because you are no longer connected to the Live Messenger server." ));
|
|
}
|
|
else
|
|
{
|
|
getChatWindow()->showStatusMessage( ChatStatusBar::DefaultType, " " );
|
|
}
|
|
|
|
|
|
// Do not reactivate if there is no switchboard connection (ChatMaster will recreate it for us)
|
|
if( isEnabled && msnSwitchboardConnection_ == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ChatView::setEnabled( isEnabled );
|
|
|
|
if ( isEnabled )
|
|
{
|
|
getChatWindow()->setUIState( ChatWindow::Connected );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change the switchboard connection used by the chat window.
|
|
void Chat::setSwitchboardConnection( MsnSwitchboardConnection *newConnection )
|
|
{
|
|
// Everything is connected already.
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
if( msnSwitchboardConnection_ == 0 )
|
|
{
|
|
kmDebug() << "Switchboard was not set up.";
|
|
}
|
|
else
|
|
{
|
|
kmDebug() << "Switchboard was "
|
|
<< ( msnSwitchboardConnection_ == newConnection ? "already set" : "different" ) << "." << endl;
|
|
}
|
|
#endif
|
|
|
|
if( msnSwitchboardConnection_ != 0 )
|
|
{
|
|
// Notify that all contacts have left the chat
|
|
if( ! msnSwitchboardConnection_->isEmpty() )
|
|
{
|
|
const QStringList participants( getParticipants() );
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Removing old switchboard's participants from the chat:" << participants;
|
|
#endif
|
|
foreach( const QString &handle, participants )
|
|
{
|
|
ContactBase* contact = currentAccount_->getContactByHandle( handle );
|
|
if( contact == 0 )
|
|
{
|
|
contact = currentAccount_->addInvitedContact( handle );
|
|
}
|
|
|
|
// NOTE: This slot doesn't use the second parameter
|
|
contactsWidget_->contactLeft( contact, false );
|
|
}
|
|
}
|
|
|
|
// Delete all signals between us and the old switchboard
|
|
disconnect( this, 0, msnSwitchboardConnection_, 0 );
|
|
disconnect( msnSwitchboardConnection_, 0, contactsWidget_, 0 );
|
|
disconnect( msnSwitchboardConnection_, 0, this, 0 );
|
|
|
|
// We are switching switchboards, clean up
|
|
if( msnSwitchboardConnection_ != newConnection )
|
|
{
|
|
// Some info of the previous switchboard must be recorded to allow
|
|
// retrieving details about the chat when no switchboard is present
|
|
lastKnownContacts_ = msnSwitchboardConnection_->getContactsInChat();
|
|
|
|
// Also delete the old connection
|
|
msnSwitchboardConnection_->deleteLater();
|
|
}
|
|
}
|
|
|
|
msnSwitchboardConnection_ = newConnection;
|
|
|
|
// The old connection has been removed, we're done
|
|
if( newConnection == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Make the new signals connections
|
|
connect( msnSwitchboardConnection_, SIGNAL( contactJoinedChat(ContactBase*) ),
|
|
contactsWidget_, SLOT ( contactJoined(ContactBase*) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( contactLeftChat(ContactBase*, bool) ),
|
|
contactsWidget_, SLOT ( contactLeft(ContactBase*, bool) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( contactTyping(ContactBase*) ),
|
|
contactsWidget_, SLOT ( contactTyping(ContactBase*) ) );
|
|
|
|
connect( msnSwitchboardConnection_, SIGNAL( contactJoinedChat(ContactBase*) ),
|
|
this, SLOT ( contactJoined(ContactBase*) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( contactLeftChat(ContactBase*, bool) ),
|
|
this, SLOT ( contactLeft(ContactBase*, bool) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( contactTyping(ContactBase*) ),
|
|
this, SLOT ( contactTyping(ContactBase*) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( chatMessage(const ChatMessage&) ),
|
|
this, SLOT ( receivedMessage(const ChatMessage&) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( receivedNudge(ContactBase*) ),
|
|
this, SLOT ( slotReceivedNudge(ContactBase*) ) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( sendingFailed(const QString&,const MimeMessage&)),
|
|
this, SLOT ( slotSendingFailed(const QString&,const MimeMessage&)) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( showWarning(MsnSwitchboardConnection::WarningType,ContactBase*)),
|
|
this, SLOT ( showWarning(MsnSwitchboardConnection::WarningType,ContactBase*)) );
|
|
connect( msnSwitchboardConnection_, SIGNAL( deleteMe(MsnSwitchboardConnection*) ),
|
|
this, SLOT ( setSwitchboardConnection() ) );
|
|
|
|
|
|
// Add to the contacts widget all the contacts that are present in the new switchboard
|
|
const QStringList participants( getParticipants() );
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Adding new switchboard's participants to the chat:" << participants;
|
|
#endif
|
|
foreach( const QString &handle, participants )
|
|
{
|
|
ContactBase *contact = currentAccount_->getContactByHandle( handle );
|
|
if( contact == 0 )
|
|
{
|
|
contact = currentAccount_->addInvitedContact( handle );
|
|
}
|
|
|
|
contactJoined( contact, true /* don't notify about the join */ );
|
|
contactsWidget_->contactJoined( contact );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// A warning from the switchboard has been received, display it
|
|
void Chat::showWarning( MsnSwitchboardConnection::WarningType type, ContactBase *contact )
|
|
{
|
|
QString handle, friendlyName;
|
|
if( contact == 0 )
|
|
{
|
|
handle = currentAccount_->getHandle();
|
|
friendlyName = currentAccount_->getFriendlyName( STRING_CLEANED_ESCAPED );
|
|
}
|
|
else
|
|
{
|
|
handle = contact->getHandle();
|
|
friendlyName = contact->getFriendlyName( STRING_CLEANED_ESCAPED );
|
|
}
|
|
|
|
switch( type )
|
|
{
|
|
case MsnSwitchboardConnection::WARNING_CONNECTION_DROP:
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
true,
|
|
i18nc( "Warning message shown in chat",
|
|
"There has been a connection problem." ),
|
|
handle,
|
|
friendlyName ) );
|
|
break;
|
|
|
|
case MsnSwitchboardConnection::WARNING_TOO_MANY_EMOTICONS:
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
true,
|
|
i18nc( "Warning message shown in chat",
|
|
"There were too many different custom emoticons in your last message. Only the first 7 will be sent." ),
|
|
handle,
|
|
friendlyName ) );
|
|
break;
|
|
|
|
case MsnSwitchboardConnection::WARNING_UNSUPPORTED_VOICECLIP:
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( contact != 0 );
|
|
#endif
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
true,
|
|
i18nc( "Warning message shown in chat, %1 is the contact's friendly name",
|
|
"%1 has sent you a voice clip, but KMess does not support voice clips yet.",
|
|
friendlyName ),
|
|
handle,
|
|
friendlyName ) );
|
|
break;
|
|
|
|
case MsnSwitchboardConnection::WARNING_UNSUPPORTED_ACTIONMESSAGE:
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( contact != 0 );
|
|
#endif
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
true,
|
|
i18nc( "Warning message shown in chat, %1 is the contact's friendly name",
|
|
"%1 has sent you an action message, but KMess does not support action messages yet.",
|
|
friendlyName ),
|
|
handle,
|
|
friendlyName ) );
|
|
break;
|
|
|
|
case MsnSwitchboardConnection::WARNING_INK_UNSUPPORTED_BY_CONTACT:
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
true,
|
|
i18nc( "Warning message shown in chat",
|
|
"One or more contacts do not support the handwriting message." ),
|
|
handle,
|
|
friendlyName ) );
|
|
break;
|
|
|
|
case MsnSwitchboardConnection::WARNING_UNSUPPORTED_UNKNOWN:
|
|
#ifdef KMESSTEST
|
|
KMESS_ASSERT( contact != 0 );
|
|
#endif
|
|
default:
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
true,
|
|
i18nc( "Warning message shown in chat, %1 is the contact's friendly name",
|
|
"%1 has sent you a Live Messenger feature that KMess does not support yet.",
|
|
friendlyName ),
|
|
handle,
|
|
friendlyName ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Show a wink received by a contact
|
|
void Chat::showWink( const QString &handle, const QString &filename, const QString &animationName )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Processing received wink data.";
|
|
#endif
|
|
|
|
QString message;
|
|
const QString friendlyName( currentAccount_->getContactFriendlyNameByHandle( handle, STRING_CLEANED_ESCAPED ) );
|
|
|
|
// If we aren't showing winks, notify the user that we got one but don't actually display the animation.
|
|
if( ! currentAccount_->getShowWinks() )
|
|
{
|
|
if( animationName.isEmpty() )
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's friendly name",
|
|
"You received a wink from %1, but displaying "
|
|
"winks has been disabled. "
|
|
"You can re-enable it in the <a href='kmess://accountconfig'>"
|
|
"account settings</a>.",
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's "
|
|
"friendly name, %2 is the wink name",
|
|
"You received the "%2" wink from %1, but displaying "
|
|
"winks has been disabled. "
|
|
"You can re-enable it in the <a href='kmess://accountconfig'>"
|
|
"account settings</a>.",
|
|
friendlyName,
|
|
animationName );
|
|
}
|
|
showMessage( ChatMessage( ChatMessage::TYPE_NOTIFICATION,
|
|
ChatMessage::CONTENT_NOTIFICATION_WINK,
|
|
false,
|
|
message,
|
|
handle ) );
|
|
return;
|
|
}
|
|
|
|
// Test whether the file exists, before cabextract fails.
|
|
if( ! QFile::exists( filename ) )
|
|
{
|
|
kmWarning() << "file not found: " << filename << "!";
|
|
return;
|
|
}
|
|
|
|
QString html;
|
|
WinksWidget::CABEXTRACTOR value = WinksWidget::getHtmlFromWink( filename, html );
|
|
|
|
if( value == WinksWidget::SUCCESS )
|
|
{
|
|
if( animationName.isEmpty() )
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's friendly name",
|
|
"%1 has sent you a wink!",
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's "
|
|
"friendly name, %2 is the wink name",
|
|
"%1 has sent you a wink: "%2"!",
|
|
friendlyName,
|
|
animationName );
|
|
}
|
|
|
|
// Show a notification
|
|
receivedMessage( ChatMessage( ChatMessage::TYPE_NOTIFICATION,
|
|
ChatMessage::CONTENT_NOTIFICATION_WINK,
|
|
true,
|
|
message + html,
|
|
handle,
|
|
friendlyName ) );
|
|
return;
|
|
}
|
|
|
|
// Extraction failed
|
|
switch( value )
|
|
{
|
|
case WinksWidget::NOTINSTALLED:
|
|
if( animationName.isEmpty() )
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's friendly name",
|
|
"You received a wink from %1, but it "
|
|
"could not be displayed. Make sure you have "
|
|
"the "cabextract" program installed.",
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's "
|
|
"friendly name, %2 is the wink name",
|
|
"You received the "%2" wink from %1, but it "
|
|
"could not be displayed. Make sure you have "
|
|
"the "cabextract" program installed.",
|
|
friendlyName,
|
|
animationName );
|
|
}
|
|
break;
|
|
|
|
case WinksWidget::FAILED:
|
|
if( animationName.isEmpty() )
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's friendly name",
|
|
"You received a wink from %1, but it "
|
|
"could not be displayed. Extracting the wink "
|
|
"package with "cabextract" has failed.",
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's "
|
|
"friendly name, %2 is the wink name",
|
|
"You received the "%2" wink from %1, but it "
|
|
"could not be displayed. Extracting the wink "
|
|
"package with "cabextract" has failed.",
|
|
friendlyName,
|
|
animationName );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if( animationName.isEmpty() )
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's friendly name",
|
|
"You received a wink from %1, but it "
|
|
"could not be displayed. The data could not be "
|
|
"read.",
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
message = i18nc( "Message shown in the chat window, %1 is the contact's "
|
|
"friendly name, %2 is the wink name",
|
|
"You received the "%2" wink from %1, but it "
|
|
"could not be displayed. The data could not be "
|
|
"read.",
|
|
friendlyName,
|
|
animationName );
|
|
}
|
|
break;
|
|
}
|
|
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
false,
|
|
message,
|
|
handle ) );
|
|
}
|
|
|
|
|
|
|
|
// Display a message to notify of a contact status change
|
|
void Chat::slotContactChangedStatus()
|
|
{
|
|
ContactBase *contact = static_cast<ContactBase*>( sender() );
|
|
if( KMESS_NULL( contact ) ) return;
|
|
|
|
// Make sure this contact is in chat
|
|
if( ! isContactInChat( contact->getHandle(), false ) )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Received a status change signal from a contact not in chat!";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
QString message;
|
|
const QString &friendlyName = contact->getFriendlyName( STRING_CLEANED_ESCAPED );
|
|
|
|
// Display a message when the user goes offline.
|
|
if( contact->isOffline() )
|
|
{
|
|
// Show a different one depending if this is an exclusive chat (which means we send offline IMs)
|
|
if( getParticipants().size() == 1 )
|
|
{
|
|
message = i18n( "%1 has gone offline. Any messages you send will be delivered the next time he or she logs in.",
|
|
friendlyName );
|
|
}
|
|
else if( currentAccount_->getShowSessionInfo() )
|
|
{
|
|
message = i18n( "%1 has gone offline.",
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if( currentAccount_->getShowSessionInfo() )
|
|
{
|
|
message = i18n( "%1 has changed his or her status to "%2".",
|
|
friendlyName,
|
|
MsnStatus::getName( contact->getStatus() ) );
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
receivedMessage( ChatMessage( ChatMessage::TYPE_PRESENCE,
|
|
ChatMessage::CONTENT_PRESENCE_STATUS,
|
|
true,
|
|
message,
|
|
contact->getHandle() ) );
|
|
}
|
|
|
|
|
|
|
|
// Notify the user of a received nudge
|
|
void Chat::slotReceivedNudge( ContactBase *contact )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Received a nudge.";
|
|
#endif
|
|
|
|
// Get friendly name
|
|
const QString& friendlyName = contact->getFriendlyName( STRING_CLEANED_ESCAPED );
|
|
|
|
ChatMessage message( ChatMessage::TYPE_NOTIFICATION,
|
|
ChatMessage::CONTENT_NOTIFICATION_NUDGE,
|
|
true,
|
|
i18n( "%1 has sent you a nudge!", friendlyName ),
|
|
contact->getHandle(),
|
|
friendlyName );
|
|
|
|
receivedMessage( message );
|
|
|
|
|
|
// Emit the new message signal so that the user will receive a notification
|
|
emit gotChatMessage( message, this );
|
|
|
|
// Start the buzz effect.
|
|
emit gotNudge();
|
|
}
|
|
|
|
|
|
|
|
// Signal that a message could not be sent to the switchboard
|
|
void Chat::slotSendingFailed( const QString &handle, const MimeMessage &message )
|
|
{
|
|
const QString contentType( message.getValue( "Content-Type" ) );
|
|
QString errorMessage;
|
|
QString friendlyName;
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Showing a sending failure notice.";
|
|
#endif
|
|
|
|
// It's possible that a message could not be delivered to any of the contacts in chat:
|
|
// in this case, the handle is *
|
|
if( handle == "*" )
|
|
{
|
|
friendlyName = i18nc( "Phrase to be inserted in place of a contact name, when a message can't be delivered "
|
|
"to any of the recipients",
|
|
"all contacts" );
|
|
}
|
|
else
|
|
{
|
|
friendlyName = currentAccount_->getContactFriendlyNameByHandle( handle, STRING_CLEANED_ESCAPED );
|
|
}
|
|
|
|
if( contentType.startsWith( "text/x-msnmsgr-datacast" ) )
|
|
{
|
|
switch( message.getValue( "ID" ).toInt() )
|
|
{
|
|
case 1: // Nudge
|
|
errorMessage = i18nc( "Error message shown in chat, %1 is the contact's friendly name",
|
|
"Failed to send the nudge to %1.",
|
|
friendlyName );
|
|
break;
|
|
case 2: // Wink
|
|
errorMessage = i18nc( "Error message shown in chat, %1 is the contact's friendly name",
|
|
"Failed to send the wink to %1.",
|
|
friendlyName );
|
|
break;
|
|
default: // Unsupported datacast?!
|
|
kmWarning() << "Failed sending datacast message: no sending failure chat message available!";
|
|
return;
|
|
}
|
|
}
|
|
else if( contentType.startsWith( "image/gif" ) || contentType.startsWith( "application/x-ms-ink" ) )
|
|
{
|
|
errorMessage = i18nc( "Error message shown in chat, %1 is the contact's friendly name", "Failed to send the handwritten message to %1.",
|
|
friendlyName );
|
|
}
|
|
else if( contentType.startsWith( "text/plain" ) && ! message.getBody().isEmpty() )
|
|
{
|
|
QString body( message.getBody().toHtmlEscaped() );
|
|
|
|
// Cut down the message a bit if needed
|
|
if( body.length() > 16 )
|
|
{
|
|
body.truncate( 16 );
|
|
body += "...";
|
|
}
|
|
|
|
errorMessage = i18nc( "Error message shown in chat, %1 is the sent message, %2 is the contact's friendly name",
|
|
"Failed to send the message to %2:<br/>%1",
|
|
body,
|
|
friendlyName );
|
|
}
|
|
else
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Not displaying a sending failure notice for messages of type" << contentType;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_SYSTEM_ERROR,
|
|
false,
|
|
errorMessage,
|
|
handle,
|
|
friendlyName ) );
|
|
}
|
|
|
|
|
|
|
|
// Send a file to a contact.
|
|
void Chat::startFileTransfer( QList<QUrl> fileList )
|
|
{
|
|
if( KMESS_NULL(msnSwitchboardConnection_) ) return;
|
|
|
|
// Ask for a contact
|
|
const QString handle( chooseContact() );
|
|
|
|
// If no contact can be chosen, stop
|
|
if( handle.isEmpty() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_FILETRANSFER
|
|
kmDebug() << "Unable to find a contact to send files to. Aborting.";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_FILETRANSFER
|
|
kmDebug() << "File list contents:" << fileList;
|
|
kmDebug() << "Chosen handle:" << handle;
|
|
#endif
|
|
|
|
if( fileList.isEmpty() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_FILETRANSFER
|
|
kmDebug() << "Asking for a file to send";
|
|
#endif
|
|
|
|
// If no file names were given, ask for a filename
|
|
QUrl fileName( QFileDialog::getOpenFileUrl( getChatWindow() ) );
|
|
|
|
// Stop if no file was selected
|
|
if( fileName.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
fileList.append( fileName );
|
|
}
|
|
|
|
foreach( const QUrl &fileUrl, fileList )
|
|
{
|
|
QUrl localFileUrl( fileUrl );
|
|
|
|
// If the url can't be translated to a local file, copy it to disk
|
|
if( ! localFileUrl.isLocalFile() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_FILETRANSFER
|
|
kmDebug() << "Not a local file, copying it locally.";
|
|
#endif
|
|
// Download the remote file to a temporary folder
|
|
QString localFilePath;
|
|
if( ! KMessShared::downloadToLocalFile( fileUrl, localFilePath ) )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_FILETRANSFER
|
|
kmDebug() << "Copy failed.";
|
|
#endif
|
|
// Show a message to the user
|
|
showMessage( ChatMessage( ChatMessage::TYPE_SYSTEM,
|
|
ChatMessage::CONTENT_APP_FAILED,
|
|
false,
|
|
i18n( "The file "%1" could not be found on "
|
|
"your computer, and the download failed.",
|
|
localFileUrl.toDisplayString() ),
|
|
handle ) );
|
|
continue;
|
|
}
|
|
|
|
// TODO Remove the temporary file after the transfer
|
|
localFileUrl = QUrl( QUrl::fromLocalFile( localFilePath ) );
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_FILETRANSFER
|
|
kmDebug() << "Original file URL:" << fileUrl << "-- Local file URL:" << localFileUrl;
|
|
#endif
|
|
|
|
// Ask the ChatMaster to initiate the file transfer. If there are multiple contacts
|
|
// in this chat, it needs to create a new chat session for that contact.
|
|
emit requestFileTransfer( handle, localFileUrl.toLocalFile() );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Send a nudge to a contact
|
|
void Chat::slotSendNudge()
|
|
{
|
|
if( msnSwitchboardConnection_ == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Show a more detailed message if we're in an active chat
|
|
QString messageText;
|
|
const QStringList &contactsInChat = msnSwitchboardConnection_->getContactsInChat();
|
|
if( contactsInChat.count() == 1 && msnSwitchboardConnection_->isConnected() )
|
|
{
|
|
messageText = i18nc( "Message shown in chat window, %1 is the contact's friendly name",
|
|
"You have sent a nudge to %1!",
|
|
currentAccount_->getContactFriendlyNameByHandle( contactsInChat.first(), STRING_CLEANED_ESCAPED ) );
|
|
}
|
|
else
|
|
{
|
|
// Chat is either empty (should resume), or a multi-chat
|
|
messageText = i18n( "You have sent a nudge!" );
|
|
}
|
|
|
|
// Display message
|
|
ChatMessage message( ChatMessage::TYPE_NOTIFICATION,
|
|
ChatMessage::CONTENT_NOTIFICATION_NUDGE,
|
|
false,
|
|
messageText,
|
|
QString() );
|
|
showMessage( message );
|
|
|
|
// Send nudge
|
|
msnSwitchboardConnection_->sendNudge();
|
|
|
|
// Shake the window
|
|
emit gotNudge();
|
|
}
|
|
|
|
|
|
|
|
// Start a chat
|
|
void Chat::startChat()
|
|
{
|
|
if( KMESS_NULL(msnSwitchboardConnection_) )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmWarning() << "Tried to open a chat without a switchboard!";
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Opening a chat of type:" << ( msnSwitchboardConnection_->isInactive() ? "Offline" : "Active" );
|
|
#endif
|
|
|
|
// Add the participant contacts to the chat, even if they're offline
|
|
const QStringList participants( msnSwitchboardConnection_->getContactsInChat() );
|
|
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Adding initial chat participants to the chat:" << participants;
|
|
#endif
|
|
|
|
foreach( const QString &handle, participants )
|
|
{
|
|
ContactBase *contact = currentAccount_->getContactByHandle( handle );
|
|
if( contact == 0 )
|
|
{
|
|
contact = currentAccount_->addInvitedContact( handle );
|
|
}
|
|
contactsWidget_->contactJoined( contact );
|
|
}
|
|
|
|
if( participants.isEmpty() )
|
|
{
|
|
kmWarning() << "The list of participants is empty!";
|
|
return;
|
|
}
|
|
|
|
|
|
const QString handle( participants.first() );
|
|
|
|
// Show the last recorded conversation, if any is present
|
|
if( participants.count() == 1 )
|
|
{
|
|
QString lastChat( ChatHistoryManager::lastChatLog( handle ) );
|
|
if( ! lastChat.isEmpty() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Adding last chat log into the chat window";
|
|
#endif
|
|
|
|
setContents( lastChat );
|
|
}
|
|
}
|
|
|
|
// Start up the log writer
|
|
if( currentAccount_->getSaveChats() )
|
|
{
|
|
logWriter_ = ChatHistoryManager::getWriter( handle );
|
|
}
|
|
|
|
// if this is an offline chat, inform the user.
|
|
if( getParticipants().size() == 1 && msnSwitchboardConnection_->isInactive() )
|
|
{
|
|
ContactBase *contact = currentAccount_->getContactByHandle( handle );
|
|
if( contact && contact->isOffline() )
|
|
{
|
|
#ifdef KMESSDEBUG_CHAT_GENERAL
|
|
kmDebug() << "Initial chat participant" << handle << "is offline; pretending the contact joined";
|
|
#endif
|
|
contactJoined( contact, true );
|
|
|
|
receivedMessage( ChatMessage( ChatMessage::TYPE_PRESENCE,
|
|
ChatMessage::CONTENT_SYSTEM_NOTICE,
|
|
false,
|
|
i18n( "%1 is currently offline. Any messages you send will be delivered the next time he or she logs in.",
|
|
contact->getFriendlyName( STRING_CLEANED_ESCAPED ) ),
|
|
handle ) );
|
|
}
|
|
}
|
|
|
|
scrollToBottom( true /* forced scrolling */ );
|
|
}
|