~ubuntu-branches/ubuntu/trusty/ktp-text-ui/trusty-proposed

« back to all changes in this revision

Viewing changes to lib/chat-widget.cpp

  • Committer: Package Import Robot
  • Author(s): Rohan Garg
  • Date: 2013-10-29 12:47:49 UTC
  • mto: (21.1.1 utopic-proposed)
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: package-import@ubuntu.com-20131029124749-gvemwfc9jnpij4g8
Tags: upstream-0.7.0
ImportĀ upstreamĀ versionĀ 0.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
#include "channel-contact-model.h"
28
28
#include "logmanager.h"
29
29
#include "notify-filter.h"
 
30
#include "text-chat-config.h"
30
31
 
31
32
#include <QtGui/QKeyEvent>
32
33
#include <QtGui/QAction>
50
51
#include <TelepathyQt/Presence>
51
52
#include <TelepathyQt/PendingChannelRequest>
52
53
#include <TelepathyQt/OutgoingFileTransferChannel>
53
 
#include <TelepathyLoggerQt4/TextEvent>
54
54
 
55
55
#include <KTp/presence.h>
56
56
#include <KTp/actions.h>
57
57
#include <KTp/message-processor.h>
58
58
 
 
59
#include <sonnet/speller.h>
 
60
 
59
61
class ChatWidgetPrivate
60
62
{
61
63
public:
62
64
    ChatWidgetPrivate() :
63
65
        remoteContactChatState(Tp::ChannelChatStateInactive),
64
66
        isGroupChat(false),
65
 
        logsLoaded(false)
 
67
        logsLoaded(false),
 
68
        exchangedMessagesCount(0)
66
69
    {
67
70
    }
68
71
    /** Stores whether the channel is ready with all contacts upgraded*/
69
 
    bool chatviewlInitialised;
 
72
    bool chatViewInitialized;
70
73
    Tp::ChannelChatState remoteContactChatState;
71
74
    bool isGroupChat;
72
75
    QString title;
79
82
    LogManager *logManager;
80
83
    QTimer *pausedStateTimer;
81
84
    bool logsLoaded;
 
85
    uint exchangedMessagesCount;
82
86
 
83
87
    QList< Tp::OutgoingFileTransferChannelPtr > tmpFileTransfers;
84
88
 
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>)));
105
109
 
106
110
    connect(d->account.data(), SIGNAL(currentPresenceChanged(Tp::Presence)),
107
111
            this, SLOT(currentPresenceChanged(Tp::Presence)));
109
113
    //load translations for this library. keep this before any i18n() calls in library code
110
114
    KGlobal::locale()->insertCatalog(QLatin1String("ktpchat"));
111
115
 
112
 
    d->chatviewlInitialised = false;
 
116
    d->chatViewInitialized = false;
113
117
    d->isGroupChat = (channel->targetHandleType() == Tp::HandleTypeContact ? false : true);
114
118
 
115
119
    d->ui.setupUi(this);
140
144
    setAcceptDrops(true);
141
145
 
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()));
145
149
    initChatArea();
146
150
 
147
 
    loadSpellCheckingOption();
148
 
 
149
151
    d->pausedStateTimer = new QTimer(this);
150
152
    d->pausedStateTimer->setSingleShot(true);
151
153
 
 
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();
 
157
 
152
158
    // make the sendMessageBox a focus proxy for the chatview
153
159
    d->ui.chatArea->setFocusProxy(d->ui.sendMessageBox);
154
160
 
164
170
    connect(d->pausedStateTimer, SIGNAL(timeout()), this, SLOT(onChatPausedTimerExpired()));
165
171
 
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();
173
 
    } else {
174
 
        m_previousConversationAvailable = false;
175
 
    }
 
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();
176
178
 
177
179
    d->notifyFilter = new NotifyFilter(this);
178
180
}
264
266
    setupChannelSignals();
265
267
 
266
268
    //if the UI is ready process any messages in queue
267
 
    if (d->chatviewlInitialised) {
 
269
    if (d->chatViewInitialized) {
268
270
        Q_FOREACH (const Tp::ReceivedMessage &message, d->channel->messageQueue()) {
269
271
            handleIncomingMessage(message, true);
270
272
        }
404
406
 
405
407
    KColorScheme scheme(QPalette::Active, KColorScheme::Window);
406
408
 
407
 
    if (d->remoteContactChatState == Tp::ChannelChatStateComposing) {
 
409
    if (TextChatConfig::instance()->showOthersTyping() && (d->remoteContactChatState == Tp::ChannelChatStateComposing)) {
408
410
        kDebug() << "remote is typing";
409
411
        return scheme.foreground(KColorScheme::PositiveText).color();
410
412
    }
468
470
}
469
471
 
470
472
 
471
 
void ChatWidget::onHistoryFetched(const QList<AdiumThemeContentInfo> &messages)
 
473
void ChatWidget::onHistoryFetched(const QList<KTp::Message> &messages)
472
474
{
 
475
    d->chatViewInitialized = true;
 
476
 
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));
 
484
            }
 
485
 
 
486
            d->ui.chatArea->addMessage(message);
 
487
        }
 
488
 
 
489
        if (date != QDate::currentDate()) {
 
490
            d->ui.chatArea->addStatusMessage(QDate::currentDate().toString(Qt::LocaleDate));
 
491
        }
476
492
    }
477
493
 
478
 
    d->chatviewlInitialised = true;
479
 
 
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);
492
506
{
493
507
    kDebug();
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());
498
512
    }
513
527
{
514
528
    kDebug() << title() << message.text();
515
529
 
516
 
    if (d->chatviewlInitialised) {
 
530
    if (d->chatViewInitialized) {
 
531
 
 
532
        d->exchangedMessagesCount++;
517
533
 
518
534
        //debug the message parts (looking for HTML etc)
519
535
//        Q_FOREACH(Tp::MessagePart part, message.parts())
528
544
 
529
545
        if (message.isDeliveryReport()) {
530
546
            QString text;
531
 
            AdiumThemeStatusInfo messageInfo;
532
547
            Tp::ReceivedMessage::DeliveryDetails reportDetails = message.deliveryDetails();
533
548
 
534
549
            if (reportDetails.hasDebugMessage()) {
605
620
                return;
606
621
            }
607
622
 
608
 
            messageInfo.setMessage(text);
609
 
            messageInfo.setTime(message.received());
610
 
            messageInfo.setStatus(QLatin1String("error"));
611
 
 
612
 
            d->ui.chatArea->addStatusMessage(messageInfo);
613
 
        } else if (message.messageType() == Tp::ChannelTextMessageTypeAction) {
614
 
            //a "/me " message
615
 
 
616
 
            AdiumThemeStatusInfo statusMessage;
617
 
            statusMessage.setTime(message.received());
618
 
 
619
 
            QString senderName;
620
 
            if (message.sender().isNull()) {
621
 
                senderName = message.senderNickname();
622
 
            } else {
623
 
                senderName = message.sender()->alias();
624
 
            }
625
 
 
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());
628
624
        } else {
629
 
            AdiumThemeContentInfo messageInfo(AdiumThemeMessageInfo::RemoteToLocal);
630
 
 
631
625
            KTp::Message processedMessage(KTp::MessageProcessor::instance()->processIncomingMessage(message, d->account, d->channel));
632
626
 
633
 
            // FIXME: eventually find a way to make MessageProcessor allow per
634
 
            //        instance filters.
635
627
            if (!alreadyNotified) {
636
628
                d->notifyFilter->filterMessage(processedMessage,
637
629
                                               KTp::MessageContext(d->account, d->channel));
638
630
            }
639
 
 
640
 
            messageInfo.setMessage(processedMessage.finalizedMessage());
641
 
            messageInfo.setScript(processedMessage.finalizedScript());
642
 
 
643
 
            QDateTime time = message.sent();
644
 
            if (!time.isValid()) {
645
 
                time = message.received();
646
 
            }
647
 
            messageInfo.setTime(time);
648
 
 
649
 
            if (processedMessage.property("highlight").toBool()) {
650
 
                messageInfo.appendMessageClass(QLatin1String("mention"));
651
 
            }
652
 
 
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());
656
 
            } else {
657
 
                messageInfo.setUserIconPath(message.sender()->avatarData().fileName);
658
 
                messageInfo.setSenderDisplayName(message.sender()->alias());
659
 
                messageInfo.setSenderScreenName(message.sender()->id());
660
 
            }
661
 
 
662
 
            d->ui.chatArea->addContentMessage(messageInfo);
 
631
            d->ui.chatArea->addMessage(processedMessage);
663
632
        }
664
633
 
665
634
        //if the window is on top, ack straight away. Otherwise they stay in the message queue for acking when activated..
672
641
 
673
642
}
674
643
 
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&)
676
645
{
677
 
    Tp::ContactPtr sender = d->channel->groupSelfContact();
678
 
 
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);
684
 
    }
685
 
    else {
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());
690
 
 
691
 
        messageInfo.setTime(message.sent());
692
 
 
693
 
        messageInfo.setSenderDisplayName(sender->alias());
694
 
        messageInfo.setSenderScreenName(sender->id());
695
 
        messageInfo.setUserIconPath(sender->avatarData().fileName);
696
 
        d->ui.chatArea->addContentMessage(messageInfo);
697
 
    }
698
 
 
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);
706
 
    }
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++;
709
651
}
710
652
 
711
653
void ChatWidget::chatViewReady()
712
654
{
713
 
    if (!d->logsLoaded) {
714
 
        d->logManager->fetchLast();
 
655
    disconnect(d->ui.chatArea, SIGNAL(loadFinished(bool)), this, SLOT(chatViewReady()));
 
656
 
 
657
    if (!d->logsLoaded || d->exchangedMessagesCount > 0) {
 
658
        if (d->exchangedMessagesCount == 0) {
 
659
            d->logManager->fetchScrollback();
 
660
        } else {
 
661
            d->logManager->fetchHistory(d->exchangedMessagesCount + d->logManager->scrollbackLength());
 
662
        }
715
663
    }
716
664
 
717
665
    d->logsLoaded = true;
746
694
    }
747
695
 
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()));
755
698
    }
756
699
 
757
700
    if (d->isGroupChat) {
790
733
    bool isYou = (contact == d->channel->groupSelfContact());
791
734
 
792
735
    if (isYou) {
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());
 
739
        } else {
 
740
            message = i18nc("Your presence status with status message",
 
741
                            "You are now marked as %1 - %2",
 
742
                            presence.displayString(), presence.statusMessage());
 
743
        }
794
744
    }
795
745
    else {
796
746
        if (presence.statusMessage().isEmpty()) {
805
755
 
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);
813
759
        }
814
760
    }
815
761
 
842
788
    }
843
789
 
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()));
850
792
    }
851
793
 
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());
865
807
    }
866
808
 
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);
872
810
 
873
811
    Q_EMIT contactBlockStatusChanged(blocked);
874
812
}
890
828
            d->pausedStateTimer->start(5000);
891
829
        } else {
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);
 
835
            } else {
 
836
                d->channel->requestChatState(Tp::ChannelChatStateActive);
 
837
                d->pausedStateTimer->stop();
 
838
            }
895
839
        }
896
840
    } else {
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);
939
883
    } else {
940
884
        if (configGroup.exists()) {
949
893
 
950
894
void ChatWidget::loadSpellCheckingOption()
951
895
{
 
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();
 
903
 
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");
957
909
    } else {
958
 
    spellCheckingLanguage = KGlobal::locale()->language();
 
910
        spellCheckingLanguage = Sonnet::Speller().defaultLanguage();
959
911
    }
960
912
    d->ui.sendMessageBox->setSpellCheckingLanguage(spellCheckingLanguage);
961
913
}
994
946
 
995
947
void ChatWidget::initChatArea()
996
948
{
 
949
    connect(d->ui.chatArea, SIGNAL(loadFinished(bool)), SLOT(chatViewReady()), Qt::QueuedConnection);
 
950
 
997
951
    d->ui.chatArea->load((d->isGroupChat ? AdiumThemeView::GroupChat : AdiumThemeView::SingleUserChat));
998
952
 
999
953
    AdiumThemeHeaderInfo info;
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);
1005
964
    } else {
1006
965
        Tp::ContactPtr otherContact = d->channel->targetContact();
1007
966
 
1036
995
 
1037
996
void ChatWidget::onChatPausedTimerExpired()
1038
997
{
1039
 
     d->channel->requestChatState(Tp::ChannelChatStatePaused);
 
998
     if (TextChatConfig::instance()->showMeTyping()) {
 
999
        d->channel->requestChatState(Tp::ChannelChatStatePaused);
 
1000
    } else {
 
1001
        d->channel->requestChatState(Tp::ChannelChatStateActive);
 
1002
    }
1040
1003
}
1041
1004
 
1042
1005
void ChatWidget::currentPresenceChanged(const Tp::Presence &presence)
1043
1006
{
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());
1052
1010
    }
1053
1011
}
1057
1015
    d->ui.sendMessageBox->insertPlainText(QLatin1String(" ") + emoticon);
1058
1016
    d->ui.sendMessageBox->setFocus();
1059
1017
}
 
1018
 
 
1019
void ChatWidget::reloadTheme()
 
1020
{
 
1021
    d->logsLoaded = false;
 
1022
    d->chatViewInitialized = false;
 
1023
 
 
1024
    initChatArea();
 
1025
}
 
1026
 
1060
1027
#include "chat-widget.moc"