Skip to content
Snippets Groups Projects
Select Git revision
  • fb0814759acc3cd0739d12731b51db81cf8f1992
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

Doxyfile-server

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    conversationitemdelegate.cpp 13.91 KiB
    /***************************************************************************
     * Copyright (C) 2015-2017 by Savoir-faire Linux                           *
     * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
     * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.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 3 of the License, or       *
     * (at your option) any later version.                                     *
     *                                                                         *
     * This program is distributed in the hope that it will be useful,         *
     * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
     * GNU General Public License for more details.                            *
     *                                                                         *
     * You should have received a copy of the GNU General Public License       *
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
     **************************************************************************/
    
    #include "conversationitemdelegate.h"
    
    #include <QApplication>
    #include <QPainter>
    #include <QPixmap>
    
    // Client
    #include "smartlistmodel.h"
    #include "ringthemeutils.h"
    #include "utils.h"
    #include "lrcinstance.h"
    #include "mainwindow.h"
    
    #include <ciso646>
    
    ConversationItemDelegate::ConversationItemDelegate(QObject* parent)
        : QItemDelegate(parent)
    {
    }
    
    void
    ConversationItemDelegate::paint(QPainter* painter
                            , const QStyleOptionViewItem& option
                            , const QModelIndex& index
                            ) const
    {
        QStyleOptionViewItem opt(option);
        painter->setRenderHint(QPainter::Antialiasing, true);
    
        // Not having focus removes dotted lines around the item
        if (opt.state & QStyle::State_HasFocus)
            opt.state ^= QStyle::State_HasFocus;
    
        auto isContextMenuOpen = index.data(static_cast<int>(SmartListModel::Role::ContextMenuOpen)).value<bool>();
        bool selected = false;
        if (option.state & QStyle::State_Selected) {
            selected = true;
            opt.state ^= QStyle::State_Selected;
        } else if (!isContextMenuOpen) {
            highlightMap_[index.row()] = option.state & QStyle::State_MouseOver;
        }
    
        // One does not simply keep the highlighted state drawn when the context
        // menu is open
        auto rowHighlight = highlightMap_.find(index.row());
        if (selected) {
           painter->fillRect(option.rect, RingTheme::smartlistSelection_);
        } else if (rowHighlight != highlightMap_.end() && (*rowHighlight).second) {
           painter->fillRect(option.rect, RingTheme::smartlistHighlight_);
        }
        auto convUid = index.data(static_cast<int>(SmartListModel::Role::UID)).value<QString>().toStdString();
        auto conversation = Utils::getConversationFromUid(convUid, *LRCInstance::getCurrentConversationModel());
        if (LRCInstance::getCurrentCallModel()->hasCall(conversation->callId)) {
            auto color = QColor(RingTheme::blue_.lighter(180)); color.setAlpha(128);
            painter->fillRect(option.rect, color);
        }
    
        QRect &rect = opt.rect;
    
        // Avatar drawing
        opt.decorationSize = QSize(sizeImage_, sizeImage_);
        opt.decorationPosition = QStyleOptionViewItem::Left;
        opt.decorationAlignment = Qt::AlignCenter;
    
        QRect rectAvatar(dx_ + rect.left(), rect.top() + dy_, sizeImage_, sizeImage_);
        drawDecoration(painter, opt, rectAvatar,
                       QPixmap::fromImage(index.data(Qt::DecorationRole).value<QImage>())
                       .scaled(sizeImage_, sizeImage_, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    
        QFont font(painter->font());
    
        // If there's unread messages, a message count is displayed
        if (auto messageCount = index.data(static_cast<int>(SmartListModel::Role::UnreadMessagesCount)).toInt()) {
            QString messageCountText = (messageCount > 9) ? "9+" : QString::number(messageCount);
            qreal fontSize = messageCountText.count() > 1 ? 7 : 8;
            font.setPointSize(fontSize);
    
            // ellipse
            QPainterPath ellipse;
            qreal ellipseHeight = sizeImage_ / 6;
            qreal ellipseWidth = ellipseHeight;
            QPointF ellipseCenter(rectAvatar.right() - ellipseWidth, rectAvatar.top() + ellipseHeight + 1);
            QRect ellipseRect(ellipseCenter.x() - ellipseWidth, ellipseCenter.y() - ellipseHeight,
                ellipseWidth * 2, ellipseHeight * 2);
            ellipse.addRoundedRect(ellipseRect, ellipseWidth, ellipseHeight);
            painter->fillPath(ellipse, RingTheme::notificationRed_);
    
            // text
            painter->setPen(Qt::white);
            painter->setOpacity(1);
            painter->setFont(font);
            ellipseRect.setTop(ellipseRect.top() - 2);
            painter->drawText(ellipseRect, Qt::AlignCenter, messageCountText);
        }
    
        // Presence indicator
        if (index.data(static_cast<int>(SmartListModel::Role::Presence)).value<bool>()) {
            qreal radius = sizeImage_ / 6;
            QPainterPath outerCircle, innerCircle;
            QPointF center(rectAvatar.right() - radius, (rectAvatar.bottom() - radius) + 1);
            qreal outerCRadius = radius;
            qreal innerCRadius = outerCRadius * 0.75;
            outerCircle.addEllipse(center, outerCRadius, outerCRadius);
            innerCircle.addEllipse(center, innerCRadius, innerCRadius);
            painter->fillPath(outerCircle, Qt::white);
            painter->fillPath(innerCircle, RingTheme::presenceGreen_);
        }
    
        using namespace lrc::api;
        auto type = Utils::toEnum<profile::Type>(
                index.data(static_cast<int>(SmartListModel::Role::ContactType)).value<int>()
            );
        switch (type) {
        case profile::Type::RING:
        case profile::Type::TEMPORARY:
            paintRingConversationItem(painter, option, rect, index);
            break;
        case profile::Type::PENDING:
            paintRingInviteConversationItem(painter, option, rect, index);
            break;
        case profile::Type::SIP:
            break;
        default:
            paintRingConversationItem(painter, option, rect, index);
            break;
        }
    }
    
    QSize
    ConversationItemDelegate::sizeHint(const QStyleOptionViewItem& option,
                                       const QModelIndex& index) const
    {
        Q_UNUSED(option);
        Q_UNUSED(index);
        return QSize(0, cellHeight_);
    }
    
    void
    ConversationItemDelegate::paintRingConversationItem(QPainter* painter,
                                                        const QStyleOptionViewItem& option,
                                                        const QRect& rect,
                                                        const QModelIndex& index) const
    {
        Q_UNUSED(option);
        QFont font(painter->font());
        QPen pen(painter->pen());
        painter->setPen(pen);
    
        int infoTextWidthModifier = 0;
        int infoText2HeightModifier = 0;
        auto scalingRatio = MainWindow::instance().getCurrentScalingRatio();
        if (scalingRatio > 1.0) {
            font.setPointSize(fontSize_ - 2);
            infoTextWidthModifier = 12;
            infoText2HeightModifier = 2;
        } else {
            font.setPointSize(fontSize_);
            infoTextWidthModifier = 10;
        }
    
        auto leftMargin = dx_ + sizeImage_ + dx_;
        auto rightMargin = dx_;
        auto topMargin = 4;
        auto bottomMargin = 8;
    
        QRect rectName1(rect.left() + leftMargin,
                        rect.top() + topMargin,
                        rect.width() - leftMargin - infoTextWidth_ - infoTextWidthModifier,
                        rect.height() / 2 - 2);
    
        QRect rectName2(rectName1.left(),
                        rectName1.top() + rectName1.height(),
                        rectName1.width(),
                        rectName1.height() - bottomMargin);
    
        QRect rectInfo1(rectName1.left() + rectName1.width(),
                        rect.top() + topMargin,
                        infoTextWidth_ - rightMargin + infoTextWidthModifier,
                        rect.height() / 2 - 2);
    
        QRect rectInfo2(rectInfo1.left(),
                        rectInfo1.top() + rectInfo1.height() - infoText2HeightModifier,
                        rectInfo1.width(),
                        rectInfo1.height() - bottomMargin + infoText2HeightModifier);
    
        QFontMetrics fontMetrics(font);
    
        // The name is displayed at the avatar's right
        QString nameStr = index.data(static_cast<int>(SmartListModel::Role::DisplayName)).value<QString>();
        if (!nameStr.isNull()) {
            font.setItalic(false);
            font.setBold(false);
            pen.setColor(RingTheme::lightBlack_);
            painter->setPen(pen);
            painter->setFont(font);
            QString elidedNameStr = fontMetrics.elidedText(nameStr, Qt::ElideRight, rectName1.width());
            painter->drawText(rectName1, Qt::AlignVCenter | Qt::AlignLeft, elidedNameStr);
        }
    
        // Display the ID under the name
        QString idStr = index.data(static_cast<int>(SmartListModel::Role::DisplayID)).value<QString>();
        if (idStr != nameStr && !idStr.isNull()) {
            font.setItalic(false);
            font.setBold(false);
            pen.setColor(RingTheme::grey_);
            painter->setPen(pen);
            painter->setFont(font);
            idStr = fontMetrics.elidedText(idStr, Qt::ElideRight, rectName2.width());
            painter->drawText(rectName2, Qt::AlignVCenter | Qt::AlignLeft, idStr);
        }
    
        // top-right: last interaction date/time
        QString lastUsedStr = index.data(static_cast<int>(SmartListModel::Role::LastInteractionDate)).value<QString>();
        if (!lastUsedStr.isNull()) {
            font.setItalic(false);
            font.setBold(false);
            pen.setColor(RingTheme::grey_);
            painter->setPen(pen);
            painter->setFont(font);
            lastUsedStr = fontMetrics.elidedText(lastUsedStr, Qt::ElideRight, rectInfo1.width());
            painter->drawText(rectInfo1, Qt::AlignVCenter | Qt::AlignRight, lastUsedStr);
        }
    
        // bottom-right: last interaction snippet
        QString interactionStr = index.data(static_cast<int>(SmartListModel::Role::LastInteraction)).value<QString>();
        if (!interactionStr.isNull()) {
            painter->save();
            font.setWeight(QFont::ExtraLight);
            interactionStr = interactionStr.simplified();
            auto type = Utils::toEnum<lrc::api::interaction::Type>(index
                .data(static_cast<int>(SmartListModel::Role::LastInteractionType))
                .value<int>());
            if (type == lrc::api::interaction::Type::CALL ||
                type == lrc::api::interaction::Type::CONTACT) {
                font.setItalic(false);
                font.setBold(false);
                pen.setColor(RingTheme::grey_.darker(140));
                painter->setPen(pen);
                painter->setFont(font);
                // strip emojis if it's a call/contact type message
                VectorUInt emojiless;
                for (auto unicode : interactionStr.toUcs4()) {
                    if (!(unicode >= 0x1F000 && unicode <= 0x1FFFF)) {
                        emojiless.push_back(unicode);
                    }
                }
                interactionStr = QString::fromUcs4(&emojiless.at(0), emojiless.size());
            } else {
                QFont emojiMsgFont(QStringLiteral("Segoe UI Emoji"));
                emojiMsgFont.setItalic(false);
                emojiMsgFont.setBold(false);
                emojiMsgFont.setPointSize(scalingRatio > 1.0 ? fontSize_ - 2 : fontSize_);
                painter->setOpacity(0.7);
                painter->setFont(emojiMsgFont);
            }
            interactionStr = fontMetrics.elidedText(interactionStr, Qt::ElideRight, rectInfo2.width());
            painter->drawText(rectInfo2, Qt::AlignVCenter | Qt::AlignRight, interactionStr);
            painter->restore();
        }
    }
    
    void
    ConversationItemDelegate::paintRingInviteConversationItem(QPainter* painter,
                                                              const QStyleOptionViewItem& option,
                                                              const QRect& rect,
                                                              const QModelIndex& index) const
    {
        QFont font(painter->font());
        font.setPointSize(fontSize_);
        QPen pen(painter->pen());
        painter->setPen(pen);
    
        auto leftMargin = dx_ + sizeImage_ + dx_;
        auto rightMargin = dx_;
        if (option.state & QStyle::State_MouseOver) {
            rightMargin = infoTextWidth_ - dx_ * 2;
        }
        auto topMargin = 4;
        auto bottomMargin = 8;
    
        QRect rectName1(rect.left() + leftMargin,
            rect.top() + topMargin,
            rect.width() - leftMargin - rightMargin,
            rect.height() / 2 - 2);
    
        QRect rectName2(rectName1.left(),
            rectName1.top() + rectName1.height(),
            rectName1.width(),
            rectName1.height() - bottomMargin);
    
        QFontMetrics fontMetrics(font);
    
        // The name is displayed at the avatar's right
        QString nameStr = index.data(static_cast<int>(SmartListModel::Role::DisplayName)).value<QString>();
        if (!nameStr.isNull()) {
            font.setItalic(false);
            font.setBold(false);
            pen.setColor(RingTheme::lightBlack_);
            painter->setPen(pen);
            painter->setFont(font);
            QString elidedNameStr = fontMetrics.elidedText(nameStr, Qt::ElideRight, rectName1.width());
            painter->drawText(rectName1, Qt::AlignVCenter | Qt::AlignLeft, elidedNameStr);
        }
    
        // Display the ID under the name
        QString idStr = index.data(static_cast<int>(SmartListModel::Role::DisplayID)).value<QString>();
        if (idStr != nameStr && !idStr.isNull()) {
            font.setItalic(false);
            font.setBold(false);
            pen.setColor(RingTheme::grey_);
            painter->setPen(pen);
            painter->setFont(font);
            idStr = fontMetrics.elidedText(idStr, Qt::ElideRight, rectName2.width());
            painter->drawText(rectName2, Qt::AlignVCenter | Qt::AlignLeft, idStr);
        }
    }
    
    void
    ConversationItemDelegate::paintSIPConversationItem(QPainter* painter,
                                                       const QStyleOptionViewItem& option,
                                                       const QModelIndex& index) const
    {
        Q_UNUSED(painter);
        Q_UNUSED(option);
        Q_UNUSED(index);
    }