50
51
#include <TelepathyQt/Presence>
51
52
#include <TelepathyQt/PendingChannelRequest>
52
53
#include <TelepathyQt/OutgoingFileTransferChannel>
53
#include <TelepathyLoggerQt4/TextEvent>
55
55
#include <KTp/presence.h>
56
56
#include <KTp/actions.h>
57
57
#include <KTp/message-processor.h>
59
#include <sonnet/speller.h>
59
61
class ChatWidgetPrivate
62
64
ChatWidgetPrivate() :
63
65
remoteContactChatState(Tp::ChannelChatStateInactive),
64
66
isGroupChat(false),
68
exchangedMessagesCount(0)
68
71
/** Stores whether the channel is ready with all contacts upgraded*/
69
bool chatviewlInitialised;
72
bool chatViewInitialized;
70
73
Tp::ChannelChatState remoteContactChatState;
101
105
d->channel = channel;
102
106
d->account = account;
103
107
d->logManager = new LogManager(this);
104
connect(d->logManager, SIGNAL(fetched(QList<AdiumThemeContentInfo>)), SLOT(onHistoryFetched(QList<AdiumThemeContentInfo>)));
108
connect(d->logManager, SIGNAL(fetched(QList<KTp::Message>)), SLOT(onHistoryFetched(QList<KTp::Message>)));
106
110
connect(d->account.data(), SIGNAL(currentPresenceChanged(Tp::Presence)),
107
111
this, SLOT(currentPresenceChanged(Tp::Presence)));
140
144
setAcceptDrops(true);
142
146
/* Prepare the chat area */
143
connect(d->ui.chatArea, SIGNAL(loadFinished(bool)), SLOT(chatViewReady()), Qt::QueuedConnection);
144
147
connect(d->ui.chatArea, SIGNAL(zoomFactorChanged(qreal)), SIGNAL(zoomFactorChanged(qreal)));
148
connect(d->ui.chatArea, SIGNAL(textPasted()), d->ui.sendMessageBox, SLOT(pasteSelection()));
147
loadSpellCheckingOption();
149
151
d->pausedStateTimer = new QTimer(this);
150
152
d->pausedStateTimer->setSingleShot(true);
154
// Spellchecking set up will trigger textChanged() signal of d->ui.sendMessageBox
155
// and our handler checks state of the timer created above.
156
loadSpellCheckingOption();
152
158
// make the sendMessageBox a focus proxy for the chatview
153
159
d->ui.chatArea->setFocusProxy(d->ui.sendMessageBox);
164
170
connect(d->pausedStateTimer, SIGNAL(timeout()), this, SLOT(onChatPausedTimerExpired()));
166
172
// initialize LogManager
167
if (!d->isGroupChat) {
168
KConfig config(QLatin1String("ktelepathyrc"));
169
KConfigGroup tabConfig = config.group("Behavior");
170
d->logManager->setFetchAmount(tabConfig.readEntry<int>("scrollbackLength", 4));
171
d->logManager->setTextChannel(d->account, d->channel);
172
m_previousConversationAvailable = d->logManager->exists();
174
m_previousConversationAvailable = false;
173
KConfig config(QLatin1String("ktelepathyrc"));
174
KConfigGroup tabConfig = config.group("Behavior");
175
d->logManager->setScrollbackLength(tabConfig.readEntry<int>("scrollbackLength", 4));
176
d->logManager->setTextChannel(d->account, d->channel);
177
m_previousConversationAvailable = d->logManager->exists();
177
179
d->notifyFilter = new NotifyFilter(this);
471
void ChatWidget::onHistoryFetched(const QList<AdiumThemeContentInfo> &messages)
473
void ChatWidget::onHistoryFetched(const QList<KTp::Message> &messages)
475
d->chatViewInitialized = true;
473
477
kDebug() << "found" << messages.count() << "messages in history";
474
Q_FOREACH(const AdiumThemeContentInfo &message, messages) {
475
d->ui.chatArea->addContentMessage(message);
478
if (!messages.isEmpty()) {
479
QDate date = messages.first().time().date();
480
Q_FOREACH(const KTp::Message &message, messages) {
481
if (message.time().date() != date) {
482
date = message.time().date();
483
d->ui.chatArea->addStatusMessage(date.toString(Qt::LocaleDate));
486
d->ui.chatArea->addMessage(message);
489
if (date != QDate::currentDate()) {
490
d->ui.chatArea->addStatusMessage(QDate::currentDate().toString(Qt::LocaleDate));
478
d->chatviewlInitialised = true;
480
494
//process any messages we've 'missed' whilst initialising.
481
495
Q_FOREACH(const Tp::ReceivedMessage &message, d->channel->messageQueue()) {
482
496
handleIncomingMessage(message, true);
494
508
//if we're not initialised we can't have shown anything, even if we are on top, therefore ignore all requests to do so
495
if (d->chatviewlInitialised) {
509
if (d->chatViewInitialized) {
496
510
//acknowledge everything in the message queue.
497
511
d->channel->acknowledge(d->channel->messageQueue());
514
528
kDebug() << title() << message.text();
516
if (d->chatviewlInitialised) {
530
if (d->chatViewInitialized) {
532
d->exchangedMessagesCount++;
518
534
//debug the message parts (looking for HTML etc)
519
535
// Q_FOREACH(Tp::MessagePart part, message.parts())
608
messageInfo.setMessage(text);
609
messageInfo.setTime(message.received());
610
messageInfo.setStatus(QLatin1String("error"));
612
d->ui.chatArea->addStatusMessage(messageInfo);
613
} else if (message.messageType() == Tp::ChannelTextMessageTypeAction) {
616
AdiumThemeStatusInfo statusMessage;
617
statusMessage.setTime(message.received());
620
if (message.sender().isNull()) {
621
senderName = message.senderNickname();
623
senderName = message.sender()->alias();
626
statusMessage.setMessage(QString::fromLatin1("%1 %2").arg(senderName, message.text()));
627
d->ui.chatArea->addStatusMessage(statusMessage);
623
d->ui.chatArea->addStatusMessage(text, message.received());
629
AdiumThemeContentInfo messageInfo(AdiumThemeMessageInfo::RemoteToLocal);
631
625
KTp::Message processedMessage(KTp::MessageProcessor::instance()->processIncomingMessage(message, d->account, d->channel));
633
// FIXME: eventually find a way to make MessageProcessor allow per
635
627
if (!alreadyNotified) {
636
628
d->notifyFilter->filterMessage(processedMessage,
637
629
KTp::MessageContext(d->account, d->channel));
640
messageInfo.setMessage(processedMessage.finalizedMessage());
641
messageInfo.setScript(processedMessage.finalizedScript());
643
QDateTime time = message.sent();
644
if (!time.isValid()) {
645
time = message.received();
647
messageInfo.setTime(time);
649
if (processedMessage.property("highlight").toBool()) {
650
messageInfo.appendMessageClass(QLatin1String("mention"));
653
//sender can have just an ID or be a full contactPtr. Use full contact info if available.
654
if (message.sender().isNull()) {
655
messageInfo.setSenderDisplayName(message.senderNickname());
657
messageInfo.setUserIconPath(message.sender()->avatarData().fileName);
658
messageInfo.setSenderDisplayName(message.sender()->alias());
659
messageInfo.setSenderScreenName(message.sender()->id());
662
d->ui.chatArea->addContentMessage(messageInfo);
631
d->ui.chatArea->addMessage(processedMessage);
665
634
//if the window is on top, ack straight away. Otherwise they stay in the message queue for acking when activated..
675
void ChatWidget::handleMessageSent(const Tp::Message &message, Tp::MessageSendingFlags, const QString&) /*Not sure what these other args are for*/
644
void ChatWidget::handleMessageSent(const Tp::Message &message, Tp::MessageSendingFlags, const QString&)
677
Tp::ContactPtr sender = d->channel->groupSelfContact();
679
if (message.messageType() == Tp::ChannelTextMessageTypeAction) {
680
AdiumThemeStatusInfo statusMessage;
681
statusMessage.setTime(message.sent());
682
statusMessage.setMessage(QString::fromLatin1("%1 %2").arg(sender->alias(), message.text()));
683
d->ui.chatArea->addStatusMessage(statusMessage);
686
AdiumThemeContentInfo messageInfo(AdiumThemeMessageInfo::LocalToRemote);
687
KTp::Message processedMessage(KTp::MessageProcessor::instance()->processIncomingMessage(message, d->account, d->channel));
688
messageInfo.setMessage(processedMessage.finalizedMessage());
689
messageInfo.setScript(processedMessage.finalizedScript());
691
messageInfo.setTime(message.sent());
693
messageInfo.setSenderDisplayName(sender->alias());
694
messageInfo.setSenderScreenName(sender->id());
695
messageInfo.setUserIconPath(sender->avatarData().fileName);
696
d->ui.chatArea->addContentMessage(messageInfo);
699
//send the notification that a message has been sent
700
KNotification *notification = new KNotification(QLatin1String("kde_telepathy_outgoing"), this);
701
notification->setComponentData(d->telepathyComponentData());
702
notification->setTitle(i18n("You have sent a message"));
703
QPixmap notificationPixmap;
704
if (notificationPixmap.load(sender->avatarData().fileName)) {
705
notification->setPixmap(notificationPixmap);
707
notification->setText(message.text());
708
notification->sendEvent();
646
KTp::Message processedMessage(KTp::MessageProcessor::instance()->processIncomingMessage(message, d->account, d->channel));
647
d->notifyFilter->filterMessage(processedMessage,
648
KTp::MessageContext(d->account, d->channel));
649
d->ui.chatArea->addMessage(processedMessage);
650
d->exchangedMessagesCount++;
711
653
void ChatWidget::chatViewReady()
713
if (!d->logsLoaded) {
714
d->logManager->fetchLast();
655
disconnect(d->ui.chatArea, SIGNAL(loadFinished(bool)), this, SLOT(chatViewReady()));
657
if (!d->logsLoaded || d->exchangedMessagesCount > 0) {
658
if (d->exchangedMessagesCount == 0) {
659
d->logManager->fetchScrollback();
661
d->logManager->fetchHistory(d->exchangedMessagesCount + d->logManager->scrollbackLength());
717
665
d->logsLoaded = true;
748
696
if (state == Tp::ChannelChatStateGone) {
749
AdiumThemeStatusInfo statusMessage;
750
statusMessage.setMessage(i18n("%1 has left the chat", contact->alias()));
751
statusMessage.setService(d->channel->connection()->protocolName());
752
statusMessage.setStatus(QLatin1String("away"));
753
statusMessage.setTime(QDateTime::currentDateTime());
754
d->ui.chatArea->addStatusMessage(statusMessage);
697
d->ui.chatArea->addStatusMessage(i18n("%1 has left the chat", contact->alias()));
757
700
if (d->isGroupChat) {
790
733
bool isYou = (contact == d->channel->groupSelfContact());
793
message = i18n("You are now marked as %1", presence.displayString());
736
if (presence.statusMessage().isEmpty()) {
737
message = i18nc("Your presence status", "You are now marked as %1",
738
presence.displayString());
740
message = i18nc("Your presence status with status message",
741
"You are now marked as %1 - %2",
742
presence.displayString(), presence.statusMessage());
796
746
if (presence.statusMessage().isEmpty()) {
806
756
if (!message.isNull()) {
807
757
if (d->ui.chatArea->showPresenceChanges()) {
808
AdiumThemeStatusInfo statusMessage;
809
statusMessage.setMessage(message);
810
statusMessage.setService(d->channel->connection()->protocolName());
811
statusMessage.setTime(QDateTime::currentDateTime());
812
d->ui.chatArea->addStatusMessage(statusMessage);
758
d->ui.chatArea->addStatusMessage(message);
844
790
if (!message.isEmpty()) {
845
AdiumThemeStatusInfo statusMessage;
846
statusMessage.setMessage(message);
847
statusMessage.setService(d->channel->connection()->protocolName());
848
statusMessage.setTime(QDateTime::currentDateTime());
849
d->ui.chatArea->addStatusMessage(statusMessage);
791
d->ui.chatArea->addStatusMessage(i18n("%1 has left the chat", contact->alias()));
852
794
//if in a non-group chat situation, and the other contact has changed alias...
864
806
message = i18n("%1 is now unblocked.", contact->alias());
867
AdiumThemeStatusInfo statusMessage;
868
statusMessage.setMessage(message);
869
statusMessage.setService(d->channel->connection()->protocolName());
870
statusMessage.setTime(QDateTime::currentDateTime());
871
d->ui.chatArea->addStatusMessage(statusMessage);
809
d->ui.chatArea->addStatusMessage(message);
873
811
Q_EMIT contactBlockStatusChanged(blocked);
890
828
d->pausedStateTimer->start(5000);
892
830
//if the user has just typed some text, set state to Composing and start the timer
893
d->channel->requestChatState(Tp::ChannelChatStateComposing);
894
d->pausedStateTimer->start(5000);
831
//unless "show me typing" is off; in that case set state to Active and stop the timer
832
if (TextChatConfig::instance()->showMeTyping()) {
833
d->channel->requestChatState(Tp::ChannelChatStateComposing);
834
d->pausedStateTimer->start(5000);
836
d->channel->requestChatState(Tp::ChannelChatStateActive);
837
d->pausedStateTimer->stop();
897
841
//if the user typed no text/cleared the input field, set Active and stop the timer
934
878
QString spellCheckingLanguage = spellDictionary();
935
879
KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("ktp-text-uirc"));
936
880
KConfigGroup configGroup = config->group(d->channel->targetId());
937
if (spellCheckingLanguage != KGlobal::locale()->language()) {
881
if (spellCheckingLanguage != Sonnet::Speller().defaultLanguage()) {
938
882
configGroup.writeEntry("language", spellCheckingLanguage);
940
884
if (configGroup.exists()) {
950
894
void ChatWidget::loadSpellCheckingOption()
896
// KTextEdit::setSpellCheckingLanguage() (see call below) does not do anything if there
897
// is no highlighter created yet, and KTextEdit::setCheckSpellingEnabled() does not create
898
// it if there is no focus on widget.
899
// Therefore d->ui.sendMessageBox->setSpellCheckingLanguage() call below would be is ignored.
900
// While this looks like KTextEditor bug (espesially taking into account its documentation),
901
// just a call to KTextEditor::createHighlighter() before setting language fixes this
902
d->ui.sendMessageBox->createHighlighter();
952
904
KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("ktp-text-uirc"));
953
905
KConfigGroup configGroup = config->group(d->channel->targetId());
954
906
QString spellCheckingLanguage;
955
907
if (configGroup.exists()) {
956
908
spellCheckingLanguage = configGroup.readEntry("language");
958
spellCheckingLanguage = KGlobal::locale()->language();
910
spellCheckingLanguage = Sonnet::Speller().defaultLanguage();
960
912
d->ui.sendMessageBox->setSpellCheckingLanguage(spellCheckingLanguage);
1001
955
info.setGroupChat(d->isGroupChat);
1002
956
//normal chat - self and one other person.
1003
957
if (d->isGroupChat) {
1004
info.setChatName(d->channel->targetId());
958
// A temporary solution to display a roomname instead of a full jid
959
// This should be reworked as soon as QtTp is offering the
960
// room name property
961
QString roomName = d->channel->targetId();
962
roomName = roomName.left(roomName.indexOf(QLatin1Char('@')));
963
info.setChatName(roomName);
1006
965
Tp::ContactPtr otherContact = d->channel->targetContact();
1037
996
void ChatWidget::onChatPausedTimerExpired()
1039
d->channel->requestChatState(Tp::ChannelChatStatePaused);
998
if (TextChatConfig::instance()->showMeTyping()) {
999
d->channel->requestChatState(Tp::ChannelChatStatePaused);
1001
d->channel->requestChatState(Tp::ChannelChatStateActive);
1042
1005
void ChatWidget::currentPresenceChanged(const Tp::Presence &presence)
1044
1007
if (presence == Tp::Presence::offline()) {
1045
// show a message informing the user
1046
AdiumThemeStatusInfo statusMessage;
1047
statusMessage.setMessage(i18n("You are now offline"));
1048
statusMessage.setService(d->channel->connection()->protocolName());
1049
statusMessage.setTime(QDateTime::currentDateTime());
1050
d->ui.chatArea->addStatusMessage(statusMessage);
1008
d->ui.chatArea->addStatusMessage(i18n("You are now offline"));
1051
1009
Q_EMIT iconChanged(KTp::Presence(Tp::Presence::offline()).icon());