Warning, file /network/ruqola/src/widgets/misc/messagelistviewbase.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002    SPDX-FileCopyrightText: 2022-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "messagelistviewbase.h"
0008 #include "model/messagesmodel.h"
0009 #include "room/plugins/plugintext.h"
0010 #include "room/plugins/plugintextinterface.h"
0011 #include "room/textpluginmanager.h"
0012 #include <QAbstractItemModel>
0013 #include <QApplication>
0014 #include <QClipboard>
0015 #include <QMouseEvent>
0016 #include <QScrollBar>
0017 
0018 MessageListViewBase::MessageListViewBase(QWidget *parent)
0019     : QListView(parent)
0020 {
0021     setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::BottomEdge}));
0022     setSelectionMode(QAbstractItemView::NoSelection);
0023     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0024     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); // nicer in case of huge messages
0025     setWordWrap(true); // so that the delegate sizeHint is called again when the width changes
0026     // only the lineedit takes focus
0027     setFocusPolicy(Qt::NoFocus);
0028     scrollToBottom();
0029     setMouseTracking(true);
0030 
0031     const QVector<PluginText *> plugins = TextPluginManager::self()->pluginsList();
0032     for (PluginText *plugin : plugins) {
0033         connect(plugin, &PluginText::errorMessage, this, &MessageListViewBase::errorMessage);
0034         connect(plugin, &PluginText::successMessage, this, &MessageListViewBase::successMessage);
0035         mPluginTextInterface.append(plugin->createInterface(this));
0036     }
0037 }
0038 
0039 MessageListViewBase::~MessageListViewBase()
0040 {
0041     qDeleteAll(mPluginTextInterface);
0042 }
0043 
0044 void MessageListViewBase::resizeEvent(QResizeEvent *ev)
0045 {
0046     QListView::resizeEvent(ev);
0047 
0048     // Fix not being really at bottom when the view gets reduced by the header widget becoming taller
0049     checkIfAtBottom();
0050     maybeScrollToBottom(); // this forces a layout in QAIV, which then changes the vbar max value
0051     updateVerticalPageStep();
0052     Q_EMIT needToClearSizeHintCache();
0053 }
0054 
0055 void MessageListViewBase::checkIfAtBottom()
0056 {
0057     auto *vbar = verticalScrollBar();
0058     mAtBottom = vbar->value() == vbar->maximum();
0059 }
0060 
0061 void MessageListViewBase::maybeScrollToBottom()
0062 {
0063     if (mAtBottom) {
0064         scrollToBottom();
0065     }
0066 }
0067 
0068 void MessageListViewBase::updateVerticalPageStep()
0069 {
0070     verticalScrollBar()->setPageStep(viewport()->height() * 3 / 4);
0071 }
0072 
0073 void MessageListViewBase::handleMouseEvent(QMouseEvent *event)
0074 {
0075     const QPersistentModelIndex index = indexAt(event->pos());
0076     if (index.isValid()) {
0077         // When the cursor hovers another message, hide/show the reaction icon accordingly
0078         if (mCurrentIndex != index) {
0079             if (mCurrentIndex.isValid()) {
0080                 auto lastModel = const_cast<QAbstractItemModel *>(mCurrentIndex.model());
0081                 lastModel->setData(mCurrentIndex, false, MessagesModel::HoverHighLight);
0082             }
0083             mCurrentIndex = index;
0084             auto model = const_cast<QAbstractItemModel *>(mCurrentIndex.model());
0085             model->setData(mCurrentIndex, true, MessagesModel::HoverHighLight);
0086         }
0087 
0088         QStyleOptionViewItem options = listViewOptions();
0089         options.rect = visualRect(mCurrentIndex);
0090         if (mouseEvent(event, options, mCurrentIndex)) {
0091             update(mCurrentIndex);
0092         }
0093     }
0094 }
0095 
0096 void MessageListViewBase::mouseReleaseEvent(QMouseEvent *event)
0097 {
0098     handleMouseEvent(event);
0099 }
0100 
0101 void MessageListViewBase::mouseDoubleClickEvent(QMouseEvent *event)
0102 {
0103     handleMouseEvent(event);
0104 }
0105 
0106 void MessageListViewBase::mousePressEvent(QMouseEvent *event)
0107 {
0108     mPressedPosition = event->pos();
0109     handleMouseEvent(event);
0110 }
0111 
0112 void MessageListViewBase::mouseMoveEvent(QMouseEvent *event)
0113 {
0114     // Drag support
0115     const int distance = (event->pos() - mPressedPosition).manhattanLength();
0116     if (distance > QApplication::startDragDistance()) {
0117         mPressedPosition = {};
0118         const QPersistentModelIndex index = indexAt(event->pos());
0119         if (index.isValid()) {
0120             QStyleOptionViewItem options = listViewOptions();
0121             options.rect = visualRect(index);
0122             if (maybeStartDrag(event, options, index)) {
0123                 return;
0124             }
0125         }
0126     }
0127     handleMouseEvent(event);
0128 }
0129 
0130 void MessageListViewBase::leaveEvent(QEvent *event)
0131 {
0132     if (mCurrentIndex.isValid()) {
0133         auto lastModel = const_cast<QAbstractItemModel *>(mCurrentIndex.model());
0134         lastModel->setData(mCurrentIndex, false, MessagesModel::HoverHighLight);
0135         mCurrentIndex = QPersistentModelIndex();
0136     }
0137     QListView::leaveEvent(event);
0138 }
0139 
0140 QStyleOptionViewItem MessageListViewBase::listViewOptions() const
0141 {
0142     QStyleOptionViewItem option;
0143     initViewItemOption(&option);
0144     return option;
0145 }
0146 
0147 bool MessageListViewBase::maybeStartDrag(QMouseEvent *event, const QStyleOptionViewItem &option, const QModelIndex &index)
0148 {
0149     Q_UNUSED(event);
0150     Q_UNUSED(option);
0151     Q_UNUSED(index);
0152     return false;
0153 }
0154 
0155 bool MessageListViewBase::mouseEvent(QMouseEvent *event, const QStyleOptionViewItem &option, const QModelIndex &index)
0156 {
0157     Q_UNUSED(event);
0158     Q_UNUSED(option);
0159     Q_UNUSED(index);
0160     return false;
0161 }
0162 
0163 void MessageListViewBase::addTextPlugins(QMenu *menu, const QString &selectedText)
0164 {
0165     for (PluginTextInterface *interface : std::as_const(mPluginTextInterface)) {
0166         interface->setSelectedText(selectedText);
0167         interface->addAction(menu);
0168     }
0169 }
0170 
0171 QString MessageListViewBase::selectedText(const QModelIndex &index)
0172 {
0173     Q_UNUSED(index);
0174     return {};
0175 }
0176 
0177 void MessageListViewBase::copyMessageToClipboard(const QModelIndex &index)
0178 {
0179     const QString messageText = selectedText(index);
0180     if (messageText.isEmpty()) {
0181         return;
0182     }
0183     QClipboard *clip = QApplication::clipboard();
0184     clip->setText(messageText, QClipboard::Clipboard);
0185     clip->setText(messageText, QClipboard::Selection);
0186 }
0187 
0188 #include "moc_messagelistviewbase.cpp"