Newer
Older
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* 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/>.
*/
Max Sandholm
committed
#include "MainWindow.h"
#include "MatrixClient.h"
#include "OverlayModal.h"
#include "RoomInfoListItem.h"
#include "RoomList.h"
#include "RoomSettings.h"
#include "RoomState.h"
#include "UserSettingsPage.h"
RoomList::RoomList(QSharedPointer<MatrixClient> client,
QSharedPointer<UserSettings> userSettings,
QWidget *parent)
, userSettings_{userSettings}
setStyleSheet("border: none;");
topLayout_ = new QVBoxLayout(this);
topLayout_->setSpacing(0);
topLayout_->setMargin(0);
scrollArea_ = new QScrollArea(this);
scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
scrollArea_->setWidgetResizable(true);
scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);
scrollAreaContents_ = new QWidget(this);
contentsLayout_ = new QVBoxLayout(scrollAreaContents_);
contentsLayout_->setSpacing(0);
contentsLayout_->setMargin(0);
contentsLayout_->addStretch(1);
scrollArea_->setWidget(scrollAreaContents_);
topLayout_->addWidget(scrollArea_);
connect(client_.data(),
[=](const QString &room_id,
const QPixmap &img,
const QString &url,
const QByteArray &data) {
if (!cache_.isNull())
cache_->saveImage(url, data);
updateRoomAvatar(room_id, img);
});
Max Sandholm
committed
void
RoomList::addRoom(const QMap<QString, QSharedPointer<RoomSettings>> &settings,
Max Sandholm
committed
const RoomState &state,
const QString &room_id)
{
RoomInfoListItem *room_item =
new RoomInfoListItem(settings[room_id], state, room_id, scrollArea_);
Max Sandholm
committed
connect(room_item, &RoomInfoListItem::clicked, this, &RoomList::highlightSelectedRoom);
connect(room_item, &RoomInfoListItem::leaveRoom, this, &RoomList::openLeaveRoomDialog);
rooms_.insert(room_id, QSharedPointer<RoomInfoListItem>(room_item));
if (!state.getAvatar().toString().isEmpty())
updateAvatar(room_id, state.getAvatar().toString());
Max Sandholm
committed
int pos = contentsLayout_->count() - 1;
contentsLayout_->insertWidget(pos, room_item);
Max Sandholm
committed
}
void
RoomList::updateAvatar(const QString &room_id, const QString &url)
{
if (url.isEmpty())
return;
QByteArray savedImgData;
if (!cache_.isNull())
savedImgData = cache_->image(url);
if (savedImgData.isEmpty()) {
client_->fetchRoomAvatar(room_id, url);
} else {
QPixmap img;
img.loadFromData(savedImgData);
updateRoomAvatar(room_id, img);
}
}
Max Sandholm
committed
void
RoomList::removeRoom(const QString &room_id, bool reset)
{
rooms_.remove(room_id);
if (rooms_.isEmpty() || !reset)
return;
auto first_room = rooms_.first();
first_room->setPressedState(true);
emit roomChanged(rooms_.firstKey());
}
void
RoomList::updateUnreadMessageCount(const QString &roomid, int count)
if (!rooms_.contains(roomid)) {
qWarning() << "UpdateUnreadMessageCount: Unknown roomid";
return;
}
rooms_[roomid]->updateUnreadMessageCount(count);
void
RoomList::calculateUnreadMessageCount()
for (const auto &room : rooms_)
total_unread_msgs += room->unreadMessageCount();
emit totalUnreadMessageCountUpdated(total_unread_msgs);
void
RoomList::setInitialRooms(const QMap<QString, QSharedPointer<RoomSettings>> &settings,
if (settings.size() != states.size()) {
qWarning() << "Initializing room list";
qWarning() << "Different number of room states and room settings";
return;
}
for (auto it = states.constBegin(); it != states.constEnd(); ++it) {
const auto room_id = it.key();
const auto state = it.value();
addRoom(settings, state, room_id);
auto first_room = rooms_.first();
first_room->setPressedState(true);
Max Sandholm
committed
void
RoomList::openLeaveRoomDialog(const QString &room_id)
{
if (leaveRoomDialog_.isNull()) {
leaveRoomDialog_ = QSharedPointer<dialogs::LeaveRoom>(new dialogs::LeaveRoom(this));
Max Sandholm
committed
connect(leaveRoomDialog_.data(),
&dialogs::LeaveRoom::closing,
this,
[=](bool leaving) { closeLeaveRoomDialog(leaving, room_id); });
}
if (leaveRoomModal_.isNull()) {
leaveRoomModal_ = QSharedPointer<OverlayModal>(
new OverlayModal(MainWindow::instance(), leaveRoomDialog_.data()));
leaveRoomModal_->setDuration(0);
leaveRoomModal_->setColor(QColor(30, 30, 30, 170));
}
Max Sandholm
committed
Max Sandholm
committed
}
RoomList::sync(const QMap<QString, RoomState> &states,
QMap<QString, QSharedPointer<RoomSettings>> &settings)
for (auto it = states.constBegin(); it != states.constEnd(); ++it) {
auto room_id = it.key();
auto state = it.value();
Max Sandholm
committed
if (!rooms_.contains(room_id)) {
settings.insert(room_id,
QSharedPointer<RoomSettings>(new RoomSettings(room_id)));
addRoom(settings, state, room_id);
Max Sandholm
committed
}
auto current_avatar = room->state().getAvatar();
auto new_avatar = state.getAvatar();
if (current_avatar != new_avatar && !new_avatar.toString().isEmpty())
updateAvatar(room_id, new_avatar.toString());
void
RoomList::clearRoomMessageCount(const QString &room_id)
{
if (!rooms_.contains(room_id))
return;
auto room = rooms_[room_id];
room->clearUnreadMessageCount();
calculateUnreadMessageCount();
}
void
RoomList::highlightSelectedRoom(const QString &room_id)
emit roomChanged(room_id);
if (!rooms_.contains(room_id)) {
qDebug() << "RoomList: clicked unknown roomid";
return;
}
for (auto it = rooms_.constBegin(); it != rooms_.constEnd(); ++it) {
if (it.key() != room_id) {
it.value()->setPressedState(false);
} else {
it.value()->setPressedState(true);
scrollArea_->ensureWidgetVisible(
qobject_cast<QWidget *>(it.value().data()));
}
}
void
RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
if (!rooms_.contains(roomid)) {
qWarning() << "Avatar update on non existent room" << roomid;
return;
}
rooms_.value(roomid)->setAvatar(img.toImage());
// Used to inform other widgets for the new image data.
emit roomAvatarChanged(roomid, img);
void
RoomList::updateRoomDescription(const QString &roomid, const DescInfo &info)
if (!rooms_.contains(roomid)) {
qWarning() << "Description update on non existent room" << roomid << info.body;
return;
}
rooms_.value(roomid)->setDescriptionMessage(info);
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
if (underMouse()) {
// When the user hover out of the roomlist a sort will be triggered.
isSortPending_ = true;
return;
}
isSortPending_ = false;
emit sortRoomsByLastMessage();
}
void
RoomList::sortRoomsByLastMessage()
{
if (!userSettings_->isOrderingEnabled())
return;
isSortPending_ = false;
std::multimap<uint64_t, RoomInfoListItem *, std::greater<uint64_t>> times;
for (int ii = 0; ii < contentsLayout_->count(); ++ii) {
auto room = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(ii)->widget());
if (!room)
continue;
// Not a room message.
if (room->lastMessageInfo().userid.isEmpty())
times.emplace(0, room);
else
times.emplace(room->lastMessageInfo().datetime.toMSecsSinceEpoch(), room);
}
for (auto it = times.cbegin(); it != times.cend(); ++it) {
const auto roomWidget = it->second;
const auto currentIndex = contentsLayout_->indexOf(roomWidget);
const auto newIndex = std::distance(times.cbegin(), it);
if (currentIndex == newIndex)
continue;
contentsLayout_->removeWidget(roomWidget);
contentsLayout_->insertWidget(newIndex, roomWidget);
}
}
void
RoomList::leaveEvent(QEvent *event)
{
if (isSortPending_)
QTimer::singleShot(700, this, &RoomList::sortRoomsByLastMessage);
QWidget::leaveEvent(event);
Max Sandholm
committed
void
RoomList::closeJoinRoomDialog(bool isJoining, QString roomAlias)
{
joinRoomModal_->fadeOut();
if (isJoining) {
client_->joinRoom(roomAlias);
}
}
void
RoomList::closeLeaveRoomDialog(bool leaving, const QString &room_id)
{
Max Sandholm
committed
if (leaving) {
client_->leaveRoom(room_id);
}
}
void
RoomList::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void
RoomList::syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &rooms)
{
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
const auto room_id = QString::fromStdString(it->first);
if (!rooms_.contains(room_id))
addInvitedRoom(room_id, it->second);
}
}
void
RoomList::addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRoom &room)
{
auto room_item = new RoomInfoListItem(room_id, room, scrollArea_);
connect(room_item, &RoomInfoListItem::acceptInvite, this, &RoomList::acceptInvite);
connect(room_item, &RoomInfoListItem::declineInvite, this, &RoomList::declineInvite);
rooms_.insert(room_id, QSharedPointer<RoomInfoListItem>(room_item));
updateAvatar(room_id, QString::fromStdString(room.avatar()));
int pos = contentsLayout_->count() - 1;
contentsLayout_->insertWidget(pos, room_item);
}