File indexing completed on 2024-06-09 04:59:24

0001 /*
0002    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "messagedelegatehelperconferencevideo.h"
0008 #include "common/delegatepaintutil.h"
0009 #include "conferencecalldialog/conferenceinfodialog.h"
0010 #include "config-ruqola.h"
0011 #include "misc/avatarcachemanager.h"
0012 #include "rocketchataccount.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QAbstractItemView>
0017 #include <QAbstractTextDocumentLayout>
0018 #include <QListView>
0019 #include <QMouseEvent>
0020 #include <QPainter>
0021 #include <QStyleOptionViewItem>
0022 #include <QToolTip>
0023 
0024 MessageDelegateHelperConferenceVideo::MessageDelegateHelperConferenceVideo(RocketChatAccount *account, QListView *view, TextSelectionImpl *textSelectionImpl)
0025     : MessageBlockDelegateHelperBase(account, view, textSelectionImpl)
0026     , mInfoIcon(QIcon::fromTheme(QStringLiteral("documentinfo")))
0027     , mAvatarCacheManager(new AvatarCacheManager(Utils::AvatarType::User, this))
0028 {
0029 }
0030 
0031 MessageDelegateHelperConferenceVideo::~MessageDelegateHelperConferenceVideo() = default;
0032 
0033 // Title [margin] <icon info>
0034 // Button join [margin] <list avatar user>
0035 //
0036 void MessageDelegateHelperConferenceVideo::draw(const Block &block,
0037                                                 QPainter *painter,
0038                                                 QRect blockRect,
0039                                                 const QModelIndex &index,
0040                                                 const QStyleOptionViewItem &option) const
0041 {
0042     Q_UNUSED(index)
0043     const ConferenceCallLayout layout = layoutConferenceCall(block, option, blockRect.width());
0044     // Draw title and buttons
0045     const int positionY = blockRect.y() + option.fontMetrics.ascent();
0046     painter->drawText(blockRect.x(), positionY, layout.title);
0047     mInfoIcon.paint(painter, layout.infoButtonRect.translated(blockRect.topLeft()));
0048 
0049     if (layout.canJoin) {
0050         // Draw join button
0051         const QPen origPen = painter->pen();
0052         const QBrush origBrush = painter->brush();
0053         const QPen buttonPen(option.palette.color(QPalette::Highlight).darker());
0054         QColor backgroundColor = option.palette.color(QPalette::Highlight);
0055         backgroundColor.setAlpha(60);
0056         const QBrush buttonBrush(backgroundColor);
0057         const QRectF joinButtonRect = layout.joinButtonRect.translated(blockRect.topLeft());
0058         // Rounded rect
0059         painter->setPen(buttonPen);
0060         painter->setBrush(buttonBrush);
0061         painter->drawRoundedRect(joinButtonRect, 5, 5);
0062         painter->setBrush(origBrush);
0063         painter->setPen(origPen);
0064         const QRectF r = joinButtonRect.adjusted((joinButtonRect.width() - layout.joinButtonTextSize.width()) / 2, 0, 0, 0);
0065         painter->drawText(r, i18n("Join"));
0066     }
0067 
0068     // Draw avatars!
0069     for (const UserLayout &userLayout : layout.usersLayout) {
0070         const QRectF avatarRect = userLayout.userAvatarRect.translated(blockRect.topLeft());
0071 #if USE_ROUNDED_RECT_PIXMAP
0072         DelegatePaintUtil::createClipRoundedRectangle(painter,
0073                                                       QRectF(avatarRect.topLeft(), avatarRect.toRect().size()),
0074                                                       avatarRect.topLeft(),
0075                                                       userLayout.avatarPixmap);
0076 #else
0077         painter->drawPixmap(avatarRect.toRect(), userLayout.avatarPixmap);
0078 #endif
0079     }
0080 }
0081 
0082 QSize MessageDelegateHelperConferenceVideo::sizeHint(const Block &block, const QModelIndex &index, int maxWidth, const QStyleOptionViewItem &option) const
0083 {
0084     Q_UNUSED(index)
0085     const ConferenceCallLayout layout = layoutConferenceCall(block, option, maxWidth);
0086     int height = layout.titleSize.height() + DelegatePaintUtil::margin();
0087     // Button
0088     if (layout.canJoin) {
0089         height += layout.joinButtonRect.height();
0090     } else if (!layout.usersLayout.isEmpty()) {
0091         height += 10 + DelegatePaintUtil::margin(); // TODO customize it
0092     }
0093     return {qMax(0, layout.titleSize.width()), height};
0094 }
0095 
0096 QPoint MessageDelegateHelperConferenceVideo::adaptMousePosition(const QPoint &pos, const Block &block, QRect blocksRect, const QStyleOptionViewItem &option)
0097 {
0098     const ConferenceCallLayout layout = layoutConferenceCall(block, option, blocksRect.width());
0099     const QPoint relativePos = pos - blocksRect.topLeft() - QPoint(0, layout.titleSize.height() + DelegatePaintUtil::margin());
0100     return relativePos;
0101 }
0102 
0103 bool MessageDelegateHelperConferenceVideo::handleMouseEvent(const Block &block,
0104                                                             QMouseEvent *mouseEvent,
0105                                                             QRect blocksRect,
0106                                                             const QStyleOptionViewItem &option,
0107                                                             const QModelIndex &index)
0108 {
0109     Q_UNUSED(index);
0110     const QEvent::Type eventType = mouseEvent->type();
0111     switch (eventType) {
0112     case QEvent::MouseButtonRelease: {
0113         const QPoint pos = mouseEvent->pos();
0114 
0115         const ConferenceCallLayout layout = layoutConferenceCall(block, option, blocksRect.width());
0116         if (layout.infoButtonRect.translated(blocksRect.topLeft()).contains(pos)) {
0117             auto parentWidget = const_cast<QWidget *>(option.widget);
0118             ConferenceInfoDialog dlg(mRocketChatAccount, parentWidget);
0119             dlg.setConferenceId(block.callId());
0120             dlg.initializeInfo();
0121             dlg.exec();
0122             return true;
0123         }
0124         if (layout.canJoin) {
0125             if (layout.joinButtonRect.translated(blocksRect.topLeft()).contains(pos)) {
0126                 if (!block.videoConferenceInfo().url().isEmpty()) {
0127                     Q_EMIT mRocketChatAccount->openLinkRequested(block.videoConferenceInfo().url());
0128                     return true;
0129                 }
0130                 return true;
0131             }
0132         }
0133         break;
0134     }
0135     default:
0136         break;
0137     }
0138 
0139     return false;
0140 }
0141 
0142 bool MessageDelegateHelperConferenceVideo::handleHelpEvent(QHelpEvent *helpEvent, QRect blockRect, const Block &block, const QStyleOptionViewItem &option)
0143 {
0144     const ConferenceCallLayout layout = layoutConferenceCall(block, option, blockRect.width());
0145     for (const UserLayout &userLayout : layout.usersLayout) {
0146         if (userLayout.userAvatarRect.translated(blockRect.topLeft()).contains(helpEvent->pos())) {
0147             QToolTip::showText(helpEvent->globalPos(), userLayout.userName, mListView);
0148             return true;
0149         }
0150     }
0151     return false;
0152 }
0153 
0154 MessageDelegateHelperConferenceVideo::ConferenceCallLayout
0155 MessageDelegateHelperConferenceVideo::layoutConferenceCall(const Block &block, const QStyleOptionViewItem &option, int blockRectWidth) const
0156 {
0157     Q_UNUSED(blockRectWidth)
0158     ConferenceCallLayout layout;
0159     layout.title = block.title();
0160     layout.titleSize = option.fontMetrics.size(Qt::TextSingleLine, layout.title);
0161     const int iconSize = option.widget->style()->pixelMetric(QStyle::PM_ButtonIconSize);
0162     layout.infoButtonRect = QRect(layout.titleSize.width() + DelegatePaintUtil::margin(), 0, iconSize, iconSize);
0163     // Join Button
0164     layout.canJoin = block.videoConferenceInfo().canJoin();
0165     if (layout.canJoin) {
0166         layout.joinButtonTextSize = option.fontMetrics.size(Qt::TextSingleLine, i18n("Join"));
0167         layout.joinButtonRect =
0168             QRect(0, layout.infoButtonRect.height() + DelegatePaintUtil::margin(), layout.joinButtonTextSize.width() * 2, layout.joinButtonTextSize.height());
0169     }
0170     // Users
0171     qreal x = 0;
0172     const QVector<User> users = block.videoConferenceInfo().users();
0173     for (const auto &user : users) {
0174         UserLayout userLayout;
0175         userLayout.userName = user.userName();
0176         userLayout.avatarPixmap = makeAvatarPixmap(userLayout.userName, option.widget, layout.titleSize.height());
0177         userLayout.userAvatarRect = QRectF((layout.canJoin ? layout.joinButtonTextSize.width() * 2 + DelegatePaintUtil::margin() : 0) + x,
0178                                            layout.infoButtonRect.height() + DelegatePaintUtil::margin(),
0179                                            iconSize,
0180                                            iconSize);
0181         layout.usersLayout.append(std::move(userLayout));
0182         x += iconSize + DelegatePaintUtil::margin();
0183     }
0184     return layout;
0185 }
0186 
0187 void MessageDelegateHelperConferenceVideo::setRocketChatAccount(RocketChatAccount *newRocketChatAccount)
0188 {
0189     mAvatarCacheManager->setCurrentRocketChatAccount(newRocketChatAccount);
0190     MessageBlockDelegateHelperBase::setRocketChatAccount(newRocketChatAccount);
0191 }
0192 
0193 QPixmap MessageDelegateHelperConferenceVideo::makeAvatarPixmap(const QString &identifier, const QWidget *widget, int maxHeight) const
0194 {
0195     Utils::AvatarInfo info;
0196     info.avatarType = Utils::AvatarType::User;
0197     info.identifier = identifier;
0198     return mAvatarCacheManager->makeAvatarPixmap(widget, info, maxHeight);
0199 }