1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
/**************************************************************************
* *
* SPDX-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de> *
* *
* SPDX-License-Identifier: GPL-3.0-or-later
* *
**************************************************************************/
#include "userlistmodel.h"
#include "../logging_categories.h"
#include <QtCore/QDebug>
#include <QtGui/QPixmap>
#include <QtGui/QPalette>
#include <QtGui/QFontMetrics>
// Injecting the dependency on a view is not so nice; but the way the model
// provides avatar decorations depends on the delegate size
#include <QtWidgets/QAbstractItemView>
#include <Quotient/connection.h>
#include <Quotient/ranges_extras.h>
#include <Quotient/room.h>
#include <Quotient/user.h>
using Quotient::RoomMember;
UserListModel::UserListModel(QAbstractItemView* parent)
: QAbstractListModel(parent), m_currentRoom(nullptr)
{ }
void UserListModel::setRoom(Quotient::Room* room)
{
if (m_currentRoom == room)
return;
using namespace Quotient;
beginResetModel();
if (m_currentRoom) {
m_currentRoom->connection()->disconnect(this);
m_currentRoom->disconnect(this);
m_memberIds.clear();
}
m_currentRoom = room;
if (m_currentRoom) {
connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::userAdded);
connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::userRemoved);
connect(m_currentRoom, &Room::memberNameAboutToUpdate, this, &UserListModel::userRemoved);
connect(m_currentRoom, &Room::memberNameUpdated, this, &UserListModel::userAdded);
connect(m_currentRoom, &Room::memberListChanged, this, &UserListModel::membersChanged);
connect(m_currentRoom, &Room::memberAvatarUpdated, this, &UserListModel::avatarChanged);
connect(m_currentRoom->connection(), &Connection::loggedOut, this,
[this] { setRoom(nullptr); });
doFilter({});
qCDebug(MODELS) << m_memberIds.count() << "member(s) in the room";
}
endResetModel();
}
Quotient::RoomMember UserListModel::userAt(QModelIndex index) const
{
if (index.row() < 0 || index.row() >= m_memberIds.size())
return {};
return m_currentRoom->member(m_memberIds.at(index.row()));
}
QVariant UserListModel::data(const QModelIndex& index, int role) const
{
if( !index.isValid() )
return QVariant();
if( index.row() >= m_memberIds.count() )
{
qCWarning(MODELS) << "UserListModel, something's wrong: index.row() >= "
"m_users.count()";
return QVariant();
}
auto m = userAt(index);
if( role == Qt::DisplayRole )
{
return m.displayName();
}
const auto* view = static_cast<const QAbstractItemView*>(parent());
if (role == Qt::DecorationRole) {
// Convert avatar image to QIcon
const auto dpi = view->devicePixelRatioF();
if (auto av = m.avatar(static_cast<int>(view->iconSize().height() * dpi), [] {});
!av.isNull()) {
av.setDevicePixelRatio(dpi);
return QIcon(QPixmap::fromImage(av));
}
// TODO: Show a different fallback icon for invited users
return QIcon::fromTheme("user-available",
QIcon(":/irc-channel-joined"));
}
if (role == Qt::ToolTipRole)
{
auto tooltip =
QStringLiteral("<b>%1</b><br>%2").arg(m.name().toHtmlEscaped(), m.id().toHtmlEscaped());
// TODO: Find a new way to determine that the user is bridged
// if (!user->bridged().isEmpty())
// tooltip += "<br>" + tr("Bridged from: %1").arg(user->bridged());
return tooltip;
}
if (role == Qt::ForegroundRole) {
// FIXME: boilerplate with TimelineItem.qml:57
const auto& palette = view->palette();
return QColor::fromHslF(static_cast<float>(m.hueF()),
1 - palette.color(QPalette::Window).saturationF(),
0.9f - 0.7f * palette.color(QPalette::Window).lightnessF(),
palette.color(QPalette::ButtonText).alphaF());
}
return QVariant();
}
int UserListModel::rowCount(const QModelIndex& parent) const
{
if( parent.isValid() )
return 0;
return m_memberIds.count();
}
void UserListModel::userAdded(const RoomMember& member)
{
auto pos = findUserPos(member.id());
if (pos != m_memberIds.size() && m_memberIds[pos] == member.id())
{
qCWarning(MODELS) << "Trying to add the user" << member.id()
<< "but it's already in the user list";
return;
}
beginInsertRows(QModelIndex(), pos, pos);
m_memberIds.insert(pos, member.id());
endInsertRows();
}
void UserListModel::userRemoved(const RoomMember& member)
{
auto pos = findUserPos(member);
if (pos == m_memberIds.size())
{
qCWarning(MODELS)
<< "Trying to remove a room member not in the user list:"
<< member.id();
return;
}
beginRemoveRows(QModelIndex(), pos, pos);
m_memberIds.removeAt(pos);
endRemoveRows();
}
void UserListModel::filter(const QString& filterString)
{
if (m_currentRoom == nullptr)
return;
beginResetModel();
doFilter(filterString);
endResetModel();
}
void UserListModel::refresh(const RoomMember& member, QVector<int> roles)
{
auto pos = findUserPos(member);
if ( pos != m_memberIds.size() )
emit dataChanged(index(pos), index(pos), roles);
else
qCWarning(MODELS)
<< "Trying to access a room member not in the user list";
}
void UserListModel::avatarChanged(const RoomMember& m)
{
refresh(m, {Qt::DecorationRole});
}
int UserListModel::findUserPos(const Quotient::RoomMember& m) const
{
return findUserPos(m.disambiguatedName());
}
int UserListModel::findUserPos(const QString& username) const
{
return static_cast<int>(Quotient::lowerBoundMemberIndex(m_memberIds, username, m_currentRoom));
}
void UserListModel::doFilter(const QString& filterString)
{
QElapsedTimer et; et.start();
auto filteredMembers = Quotient::rangeTo<QList>(
std::views::filter(m_currentRoom->joinedMembers(),
Quotient::memberMatcher(filterString, Qt::CaseInsensitive)));
std::ranges::sort(filteredMembers, Quotient::MemberSorter());
const auto sortedIds = std::views::transform(filteredMembers, &RoomMember::id);
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
m_memberIds.assign(sortedIds.begin(), sortedIds.end());
#else
m_memberIds = QList(sortedIds.begin(), sortedIds.end());
#endif
qCDebug(MODELS) << "Filtering" << m_memberIds.size() << "user(s) in"
<< m_currentRoom->displayName() << "took" << et;
}
|