File indexing completed on 2024-12-08 07:33:43

0001 // SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "completionmodel.h"
0005 #include <QDebug>
0006 
0007 #include "actionsmodel.h"
0008 #include "completionproxymodel.h"
0009 #include "customemojimodel.h"
0010 #include "emojimodel.h"
0011 #include "neochatroom.h"
0012 #include "userlistmodel.h"
0013 
0014 CompletionModel::CompletionModel(QObject *parent)
0015     : QAbstractListModel(parent)
0016     , m_filterModel(new CompletionProxyModel())
0017     , m_userListModel(new UserListModel(this))
0018     , m_emojiModel(new QConcatenateTablesProxyModel(this))
0019 {
0020     connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
0021     connect(this, &CompletionModel::roomChanged, this, [this]() {
0022         m_userListModel->setRoom(m_room);
0023     });
0024     m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
0025     m_emojiModel->addSourceModel(&EmojiModel::instance());
0026 }
0027 
0028 QString CompletionModel::text() const
0029 {
0030     return m_text;
0031 }
0032 
0033 void CompletionModel::setText(const QString &text, const QString &fullText)
0034 {
0035     m_text = text;
0036     m_fullText = fullText;
0037     Q_EMIT textChanged();
0038 }
0039 
0040 int CompletionModel::rowCount(const QModelIndex &parent) const
0041 {
0042     Q_UNUSED(parent);
0043     if (m_autoCompletionType == None) {
0044         return 0;
0045     }
0046     return m_filterModel->rowCount();
0047 }
0048 
0049 QVariant CompletionModel::data(const QModelIndex &index, int role) const
0050 {
0051     if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
0052         return {};
0053     }
0054     auto filterIndex = m_filterModel->index(index.row(), 0);
0055     if (m_autoCompletionType == User) {
0056         if (role == DisplayNameRole) {
0057             return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
0058         }
0059         if (role == SubtitleRole) {
0060             return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
0061         }
0062         if (role == IconNameRole) {
0063             return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
0064         }
0065     }
0066 
0067     if (m_autoCompletionType == Command) {
0068         if (role == DisplayNameRole) {
0069             return QStringLiteral("%1 %2").arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
0070                                                m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
0071         }
0072         if (role == SubtitleRole) {
0073             return m_filterModel->data(filterIndex, ActionsModel::Description);
0074         }
0075         if (role == IconNameRole) {
0076             return QStringLiteral("invalid");
0077         }
0078         if (role == ReplacedTextRole) {
0079             return m_filterModel->data(filterIndex, ActionsModel::Prefix);
0080         }
0081     }
0082     if (m_autoCompletionType == Room) {
0083         if (role == DisplayNameRole) {
0084             return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
0085         }
0086         if (role == SubtitleRole) {
0087             return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
0088         }
0089         if (role == IconNameRole) {
0090             auto mediaId = m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
0091             if (mediaId.isEmpty()) {
0092                 return QVariant();
0093             }
0094             return m_room->connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(mediaId)));
0095         }
0096     }
0097     if (m_autoCompletionType == Emoji) {
0098         if (role == DisplayNameRole) {
0099             return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
0100         }
0101         if (role == IconNameRole) {
0102             return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
0103         }
0104         if (role == ReplacedTextRole) {
0105             return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
0106         }
0107         if (role == SubtitleRole) {
0108             return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
0109         }
0110     }
0111 
0112     return {};
0113 }
0114 
0115 QHash<int, QByteArray> CompletionModel::roleNames() const
0116 {
0117     return {
0118         {DisplayNameRole, "displayName"},
0119         {SubtitleRole, "subtitle"},
0120         {IconNameRole, "iconName"},
0121         {ReplacedTextRole, "replacedText"},
0122     };
0123 }
0124 
0125 void CompletionModel::updateCompletion()
0126 {
0127     if (text().startsWith(QLatin1Char('@'))) {
0128         m_filterModel->setSourceModel(m_userListModel);
0129         m_filterModel->setFilterRole(UserListModel::UserIdRole);
0130         m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
0131         m_filterModel->setFullText(m_fullText);
0132         m_filterModel->setFilterText(m_text);
0133         m_autoCompletionType = User;
0134         m_filterModel->invalidate();
0135     } else if (text().startsWith(QLatin1Char('/'))) {
0136         m_filterModel->setSourceModel(&ActionsModel::instance());
0137         m_filterModel->setFilterRole(ActionsModel::Prefix);
0138         m_filterModel->setSecondaryFilterRole(-1);
0139         m_filterModel->setFullText(m_fullText);
0140         m_filterModel->setFilterText(m_text.mid(1));
0141         m_autoCompletionType = Command;
0142         m_filterModel->invalidate();
0143     } else if (text().startsWith(QLatin1Char('#'))) {
0144         m_autoCompletionType = Room;
0145         m_filterModel->setSourceModel(m_roomListModel);
0146         m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
0147         m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
0148         m_filterModel->setFullText(m_fullText);
0149         m_filterModel->setFilterText(m_text);
0150         m_filterModel->invalidate();
0151     } else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
0152                && (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
0153                    || (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
0154         m_filterModel->setSourceModel(m_emojiModel);
0155         m_autoCompletionType = Emoji;
0156         m_filterModel->setFilterRole(CustomEmojiModel::Name);
0157         m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
0158         m_filterModel->setFullText(m_fullText);
0159         m_filterModel->setFilterText(m_text);
0160         m_filterModel->invalidate();
0161     } else {
0162         m_autoCompletionType = None;
0163     }
0164     beginResetModel();
0165     endResetModel();
0166 }
0167 
0168 NeoChatRoom *CompletionModel::room() const
0169 {
0170     return m_room;
0171 }
0172 
0173 void CompletionModel::setRoom(NeoChatRoom *room)
0174 {
0175     m_room = room;
0176     Q_EMIT roomChanged();
0177 }
0178 
0179 CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const
0180 {
0181     return m_autoCompletionType;
0182 }
0183 
0184 void CompletionModel::setAutoCompletionType(AutoCompletionType autoCompletionType)
0185 {
0186     m_autoCompletionType = autoCompletionType;
0187     Q_EMIT autoCompletionTypeChanged();
0188 }
0189 
0190 RoomListModel *CompletionModel::roomListModel() const
0191 {
0192     return m_roomListModel;
0193 }
0194 
0195 void CompletionModel::setRoomListModel(RoomListModel *roomListModel)
0196 {
0197     m_roomListModel = roomListModel;
0198     Q_EMIT roomListModelChanged();
0199 }
0200 
0201 #include "moc_completionmodel.cpp"