249 lines
6.3 KiB
C++
249 lines
6.3 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop 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.
|
|
|
|
It 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.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#include "stdafx.h"
|
|
#include "dialogs/dialogs_list.h"
|
|
|
|
#include "dialogs/dialogs_layout.h"
|
|
#include "styles/style_dialogs.h"
|
|
#include "mainwidget.h"
|
|
|
|
namespace Dialogs {
|
|
|
|
List::List(SortMode sortMode)
|
|
: _last(std_::make_unique<Row>(nullptr, nullptr, nullptr, 0))
|
|
, _begin(_last.get())
|
|
, _end(_last.get())
|
|
, _sortMode(sortMode)
|
|
, _current(_last.get()) {
|
|
}
|
|
|
|
void List::adjustCurrent(int32 y, int32 h) const {
|
|
if (isEmpty()) return;
|
|
|
|
int32 pos = (y > 0) ? (y / h) : 0;
|
|
while (_current->_pos > pos && _current != _begin) {
|
|
_current = _current->_prev;
|
|
}
|
|
while (_current->_pos + 1 <= pos && _current->_next != _end) {
|
|
_current = _current->_next;
|
|
}
|
|
}
|
|
|
|
void List::paint(Painter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel, bool onlyBackground) const {
|
|
adjustCurrent(hFrom, st::dialogsRowHeight);
|
|
|
|
Row *row = _current;
|
|
p.translate(0, row->_pos * st::dialogsRowHeight);
|
|
while (row != _end && row->_pos * st::dialogsRowHeight < hTo) {
|
|
bool active = (row->history()->peer == act) || (row->history()->peer->migrateTo() && row->history()->peer->migrateTo() == act);
|
|
bool selected = (row->history()->peer == sel);
|
|
Layout::RowPainter::paint(p, row, w, active, selected, onlyBackground);
|
|
row = row->_next;
|
|
p.translate(0, st::dialogsRowHeight);
|
|
}
|
|
}
|
|
|
|
Row *List::addToEnd(History *history) {
|
|
Row *result = new Row(history, _end->_prev, _end, _end->_pos);
|
|
_end->_pos++;
|
|
if (_begin == _end) {
|
|
_begin = _current = result;
|
|
} else {
|
|
_end->_prev->_next = result;
|
|
}
|
|
_rowByPeer.insert(history->peer->id, result);
|
|
++_count;
|
|
_end->_prev = result;
|
|
if (_sortMode == SortMode::Date) {
|
|
adjustByPos(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool List::insertBefore(Row *row, Row *before) {
|
|
if (row == before) return false;
|
|
|
|
if (_current == row) {
|
|
_current = row->_prev;
|
|
}
|
|
|
|
Row *updateTill = row->_prev;
|
|
remove(row);
|
|
|
|
// insert row
|
|
row->_next = before; // update row
|
|
row->_prev = before->_prev;
|
|
row->_next->_prev = row; // update row->next
|
|
if (row->_prev) { // update row->prev
|
|
row->_prev->_next = row;
|
|
} else {
|
|
_begin = row;
|
|
}
|
|
|
|
// update y
|
|
for (Row *n = row; n != updateTill; n = n->_next) {
|
|
n->_next->_pos++;
|
|
row->_pos--;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool List::insertAfter(Row *row, Row *after) {
|
|
if (row == after) return false;
|
|
|
|
if (_current == row) {
|
|
_current = row->_next;
|
|
}
|
|
|
|
Row *updateFrom = row->_next;
|
|
remove(row);
|
|
|
|
// insert row
|
|
row->_prev = after; // update row
|
|
row->_next = after->_next;
|
|
row->_prev->_next = row; // update row->prev
|
|
row->_next->_prev = row; // update row->next
|
|
|
|
// update y
|
|
for (Row *n = updateFrom; n != row; n = n->_next) {
|
|
n->_pos--;
|
|
row->_pos++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Row *List::adjustByName(const PeerData *peer) {
|
|
if (_sortMode != SortMode::Name) return nullptr;
|
|
|
|
auto i = _rowByPeer.find(peer->id);
|
|
if (i == _rowByPeer.cend()) return nullptr;
|
|
|
|
Row *row = i.value(), *change = row;
|
|
while (change->_prev && change->_prev->history()->peer->name > peer->name) {
|
|
change = change->_prev;
|
|
}
|
|
if (!insertBefore(row, change)) {
|
|
while (change->_next != _end && change->_next->history()->peer->name < peer->name) {
|
|
change = change->_next;
|
|
}
|
|
insertAfter(row, change);
|
|
}
|
|
return row;
|
|
}
|
|
|
|
Row *List::addByName(History *history) {
|
|
if (_sortMode != SortMode::Name) return nullptr;
|
|
|
|
Row *row = addToEnd(history), *change = row;
|
|
const QString &peerName(history->peer->name);
|
|
while (change->_prev && change->_prev->history()->peer->name.compare(peerName, Qt::CaseInsensitive) > 0) {
|
|
change = change->_prev;
|
|
}
|
|
if (!insertBefore(row, change)) {
|
|
while (change->_next != _end && change->_next->history()->peer->name.compare(peerName, Qt::CaseInsensitive) < 0) {
|
|
change = change->_next;
|
|
}
|
|
insertAfter(row, change);
|
|
}
|
|
return row;
|
|
}
|
|
|
|
void List::adjustByPos(Row *row) {
|
|
if (_sortMode != SortMode::Date || !_begin) return;
|
|
|
|
Row *change = row;
|
|
if (change != _begin && _begin->history()->sortKeyInChatList() < row->history()->sortKeyInChatList()) {
|
|
change = _begin;
|
|
} else {
|
|
while (change->_prev && change->_prev->history()->sortKeyInChatList() < row->history()->sortKeyInChatList()) {
|
|
change = change->_prev;
|
|
}
|
|
}
|
|
if (!insertBefore(row, change)) {
|
|
if (change->_next != _end && _end->_prev->history()->sortKeyInChatList() > row->history()->sortKeyInChatList()) {
|
|
change = _end->_prev;
|
|
} else {
|
|
while (change->_next != _end && change->_next->history()->sortKeyInChatList() > row->history()->sortKeyInChatList()) {
|
|
change = change->_next;
|
|
}
|
|
}
|
|
insertAfter(row, change);
|
|
}
|
|
}
|
|
|
|
bool List::moveToTop(PeerId peerId) {
|
|
auto i = _rowByPeer.find(peerId);
|
|
if (i == _rowByPeer.cend()) return false;
|
|
|
|
insertBefore(i.value(), _begin);
|
|
return true;
|
|
}
|
|
|
|
bool List::del(PeerId peerId, Row *replacedBy) {
|
|
auto i = _rowByPeer.find(peerId);
|
|
if (i == _rowByPeer.cend()) return false;
|
|
|
|
Row *row = i.value();
|
|
if (App::main()) {
|
|
emit App::main()->dialogRowReplaced(row, replacedBy);
|
|
}
|
|
|
|
if (row == _current) {
|
|
_current = row->_next;
|
|
}
|
|
for (Row *change = row->_next; change != _end; change = change->_next) {
|
|
--change->_pos;
|
|
}
|
|
--_end->_pos;
|
|
remove(row);
|
|
delete row;
|
|
--_count;
|
|
_rowByPeer.erase(i);
|
|
|
|
return true;
|
|
}
|
|
|
|
void List::remove(Row *row) {
|
|
row->_next->_prev = row->_prev; // update row->next
|
|
if (row->_prev) { // update row->prev
|
|
row->_prev->_next = row->_next;
|
|
} else {
|
|
_begin = row->_next;
|
|
}
|
|
}
|
|
|
|
void List::clear() {
|
|
while (_begin != _end) {
|
|
_current = _begin;
|
|
_begin = _begin->_next;
|
|
delete _current;
|
|
}
|
|
_current = _begin;
|
|
_rowByPeer.clear();
|
|
_count = 0;
|
|
}
|
|
|
|
List::~List() {
|
|
clear();
|
|
}
|
|
|
|
} // namespace Dialogs
|