diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 6d9ca16600..a716cfbe6f 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -1070,7 +1070,7 @@ void AddSpecialBoxSearchController::addChatsContacts() { auto result = (const Dialogs::List*)nullptr; for (const auto &word : wordList) { const auto found = list->filtered(word[0]); - if (found->isEmpty()) { + if (found->empty()) { return nullptr; } if (!result || result->size() > found->size()) { diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index bc46a8f020..9ae0548ccf 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -952,7 +952,7 @@ void ShareBox::Inner::updateFilter(QString filter) { if (!_chatsIndexed->isEmpty()) { for (fi = fb; fi != fe; ++fi) { auto found = _chatsIndexed->filtered(fi->at(0)); - if (found->isEmpty()) { + if (found->empty()) { toFilter = nullptr; break; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 078cd637d6..5903c3f6ff 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -130,7 +130,7 @@ PositionChange Entry::adjustByPosInChatList( not_null indexed) { const auto lnk = mainChatListLink(list); const auto movedFrom = lnk->pos(); - indexed->adjustByPos(chatListLinks(list)); + indexed->adjustByDate(chatListLinks(list)); const auto movedTo = lnk->pos(); return { movedFrom, movedTo }; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp index 8a68400c82..f75ba99d65 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp @@ -41,7 +41,7 @@ Row *IndexedList::addByName(Key key) { return row; } - Row *result = _list.addByName(key); + const auto result = _list.addByName(key); for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = _index.find(ch); if (j == _index.cend()) { @@ -54,13 +54,13 @@ Row *IndexedList::addByName(Key key) { return result; } -void IndexedList::adjustByPos(const RowsByLetter &links) { +void IndexedList::adjustByDate(const RowsByLetter &links) { for (const auto [ch, row] : links) { if (ch == QChar(0)) { - _list.adjustByPos(row); + _list.adjustByDate(row); } else { if (auto it = _index.find(ch); it != _index.cend()) { - it->second->adjustByPos(row); + it->second->adjustByDate(row); } } } @@ -118,6 +118,8 @@ void IndexedList::peerNameChanged( void IndexedList::adjustByName( Key key, const base::flat_set &oldLetters) { + Expects(_sortMode == SortMode::Name); + const auto mainRow = _list.adjustByName(key); if (!mainRow) return; diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h index 1f951fce83..ec98bc8a6e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h @@ -20,7 +20,7 @@ public: RowsByLetter addToEnd(Key key); Row *addByName(Key key); - void adjustByPos(const RowsByLetter &links); + void adjustByDate(const RowsByLetter &links); void moveToTop(Key key); // row must belong to this indexed list all(). @@ -54,7 +54,7 @@ public: // Part of List interface is duplicated here for all() list. int size() const { return all().size(); } - bool isEmpty() const { return all().isEmpty(); } + bool isEmpty() const { return all().empty(); } bool contains(Key key) const { return all().contains(key); } Row *getRow(Key key) const { return all().getRow(key); } Row *rowAtY(int32 y, int32 h) const { return all().rowAtY(y, h); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index ed7d1cd80d..662f60edaa 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1648,7 +1648,7 @@ void DialogsInner::applyFilterUpdate(QString newFilter, bool force) { if (!_dialogs->isEmpty()) { for (fi = fb; fi != fe; ++fi) { auto found = _dialogs->filtered(fi->at(0)); - if (found->isEmpty()) { + if (found->empty()) { toFilter = nullptr; break; } @@ -1661,7 +1661,7 @@ void DialogsInner::applyFilterUpdate(QString newFilter, bool force) { if (!_contactsNoDialogs->isEmpty()) { for (fi = fb; fi != fe; ++fi) { auto found = _contactsNoDialogs->filtered(fi->at(0)); - if (found->isEmpty()) { + if (found->empty()) { toFilterContacts = nullptr; break; } @@ -2020,13 +2020,9 @@ void DialogsInner::peerSearchReceived( if (alreadyAdded(peer)) { continue; } - const auto prev = nullptr, next = nullptr; - const auto position = 0; auto row = std::make_unique( peer->owner().history(peer), - prev, - next, - position); + _filterResults.size()); const auto [i, ok] = _filterResultsGlobal.emplace( peer, std::move(row)); diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp index 73c214eb35..40eb2ce510 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp @@ -14,225 +14,137 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Dialogs { -List::List(SortMode sortMode) -: _last(std::make_unique(nullptr)) -, _begin(_last.get()) -, _end(_last.get()) -, _sortMode(sortMode) -, _current(_last.get()) { +List::List(SortMode sortMode) : _sortMode(sortMode) { } -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; - } +List::const_iterator List::cfind(Row *value) const { + return value + ? (cbegin() + value->pos()) + : cend(); } -Row *List::addToEnd(Key key) { - const auto result = new Row(key, _end->_prev, _end, _end->_pos); - _end->_pos++; - if (_begin == _end) { - _begin = _current = result; - } else { - _end->_prev->_next = result; +not_null List::addToEnd(Key key) { + if (const auto result = getRow(key)) { + return result; } - _rowByKey.emplace(key, result); - ++_count; - _end->_prev = result; + const auto result = _rowByKey.emplace( + key, + std::make_unique(key, _rows.size()) + ).first->second.get(); + _rows.push_back(result); if (_sortMode == SortMode::Date) { - adjustByPos(result); + adjustByDate(result); } return result; } -bool List::insertBefore(Row *row, Row *before) { - if (row == before) { - return false; - } - - if (_current == row) { - _current = row->_prev; - } - - const auto 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 pos - for (auto 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; - } - - const auto 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 pos - for (auto n = updateFrom; n != row; n = n->_next) { - n->_pos--; - row->_pos++; - } - return true; -} - Row *List::adjustByName(Key key) { - if (_sortMode != SortMode::Name) return nullptr; + Expects(_sortMode == SortMode::Name); - const auto i = _rowByKey.find(key); - if (i == _rowByKey.cend()) return nullptr; - - const auto row = i->second; - const auto name = key.entry()->chatListName(); - auto change = row; - while (change->_prev - && change->_prev->entry()->chatListName().compare(name, Qt::CaseInsensitive) < 0) { - change = change->_prev; - } - if (!insertBefore(row, change)) { - while (change->_next != _end - && change->_next->entry()->chatListName().compare(name, Qt::CaseInsensitive) < 0) { - change = change->_next; - } - insertAfter(row, change); - } - return row; -} - -Row *List::addByName(Key key) { - if (_sortMode != SortMode::Name) { + const auto row = getRow(key); + if (!row) { return nullptr; } - - const auto row = addToEnd(key); - auto change = row; - const auto name = key.entry()->chatListName(); - while (change->_prev - && change->_prev->entry()->chatListName().compare(name, Qt::CaseInsensitive) > 0) { - change = change->_prev; - } - if (!insertBefore(row, change)) { - while (change->_next != _end - && change->_next->entry()->chatListName().compare(name, Qt::CaseInsensitive) < 0) { - change = change->_next; - } - insertAfter(row, change); - } + adjustByName(row); return row; } -void List::adjustByPos(Row *row) { - if (_sortMode != SortMode::Date || !_begin) return; +not_null List::addByName(Key key) { + Expects(_sortMode == SortMode::Name); - Row *change = row; - if (change != _begin && _begin->sortKey() < row->sortKey()) { - change = _begin; - } else { - while (change->_prev && change->_prev->sortKey() < row->sortKey()) { - change = change->_prev; + const auto row = addToEnd(key); + adjustByName(key); + return row; +} + +void List::adjustByName(not_null row) { + Expects(row->pos() >= 0 && row->pos() < _rows.size()); + + const auto &name = row->entry()->chatListName(); + const auto index = row->pos(); + const auto i = _rows.begin() + index; + const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) { + const auto &greater = row->entry()->chatListName(); + return greater.compare(name, Qt::CaseInsensitive) >= 0; + }); + if (before != i + 1) { + rotate(i, i + 1, before); + } else if (i != _rows.begin()) { + const auto from = std::make_reverse_iterator(i); + const auto after = std::find_if(from, _rows.rend(), [&](Row *row) { + const auto &less = row->entry()->chatListName(); + return less.compare(name, Qt::CaseInsensitive) <= 0; + }).base(); + if (after != i) { + rotate(after, i, i + 1); } } - if (!insertBefore(row, change)) { - if (change->_next != _end && _end->_prev->sortKey() > row->sortKey()) { - change = _end->_prev; - } else { - while (change->_next != _end && change->_next->sortKey() > row->sortKey()) { - change = change->_next; - } +} + +void List::adjustByDate(not_null row) { + Expects(_sortMode == SortMode::Date); + + const auto key = row->sortKey(); + const auto index = row->pos(); + const auto i = _rows.begin() + index; + const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) { + return (row->sortKey() <= key); + }); + if (before != i + 1) { + rotate(i, i + 1, before); + } else { + const auto from = std::make_reverse_iterator(i); + const auto after = std::find_if(from, _rows.rend(), [&](Row *row) { + return (row->sortKey() >= key); + }).base(); + if (after != i) { + rotate(after, i, i + 1); } - insertAfter(row, change); } } bool List::moveToTop(Key key) { - auto i = _rowByKey.find(key); + const auto i = _rowByKey.find(key); if (i == _rowByKey.cend()) { return false; } - - insertBefore(i->second, _begin); + const auto index = i->second->pos(); + const auto begin = _rows.begin(); + rotate(begin, begin + index, begin + index + 1); return true; } +void List::rotate( + std::vector>::iterator first, + std::vector>::iterator middle, + std::vector>::iterator last) { + std::rotate(first, middle, last); + + auto count = (last - first); + auto index = (first - _rows.begin()); + while (count--) { + (*first++)->_pos = index++; + } +} + bool List::del(Key key, Row *replacedBy) { auto i = _rowByKey.find(key); if (i == _rowByKey.cend()) { return false; } - const auto row = i->second; + const auto row = i->second.get(); if (App::main()) { emit App::main()->dialogRowReplaced(row, replacedBy); } - if (row == _current) { - _current = row->_next; + const auto index = row->pos(); + _rows.erase(_rows.begin() + index); + for (auto i = index, count = int(_rows.size()); i != count; ++i) { + _rows[i]->_pos = i; } - for (auto change = row->_next; change != _end; change = change->_next) { - --change->_pos; - } - --_end->_pos; - remove(row); - delete row; - --_count; _rowByKey.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; - _rowByKey.clear(); - _count = 0; -} - -List::~List() { - clear(); -} - } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.h b/Telegram/SourceFiles/dialogs/dialogs_list.h index bd55962150..54b82f2d9c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_list.h @@ -21,105 +21,62 @@ public: List &operator=(const List &other) = delete; int size() const { - return _count; + return _rows.size(); } - bool isEmpty() const { - return size() == 0; + bool empty() const { + return _rows.empty(); } bool contains(Key key) const { return _rowByKey.find(key) != _rowByKey.end(); } Row *getRow(Key key) const { const auto i = _rowByKey.find(key); - return (i == _rowByKey.end()) ? nullptr : i->second.get(); + return (i != _rowByKey.end()) ? i->second.get() : nullptr; } - Row *rowAtY(int32 y, int32 h) const { - auto i = cfind(y, h); + Row *rowAtY(int y, int h) const { + const auto i = cfind(y, h); if (i == cend() || (*i)->pos() != ((y > 0) ? (y / h) : 0)) { return nullptr; } return *i; } - Row *addToEnd(Key key); + not_null addToEnd(Key key); Row *adjustByName(Key key); - Row *addByName(Key key); + not_null addByName(Key key); bool moveToTop(Key key); - void adjustByPos(Row *row); + void adjustByDate(not_null row); bool del(Key key, Row *replacedBy = nullptr); - void remove(Row *row); - void clear(); - class const_iterator { - public: - using value_type = Row*; - using pointer = Row**; - using reference = Row*&; - - explicit const_iterator(Row *p) : _p(p) { - } - inline Row* operator*() const { return _p; } - inline Row* const* operator->() const { return &_p; } - inline bool operator==(const const_iterator &other) const { return _p == other._p; } - inline bool operator!=(const const_iterator &other) const { return !(*this == other); } - inline const_iterator &operator++() { _p = next(_p); return *this; } - inline const_iterator operator++(int) { const_iterator result(*this); ++(*this); return result; } - inline const_iterator &operator--() { _p = prev(_p); return *this; } - inline const_iterator operator--(int) { const_iterator result(*this); --(*this); return result; } - inline const_iterator operator+(int j) const { const_iterator result = *this; return result += j; } - inline const_iterator operator-(int j) const { const_iterator result = *this; return result -= j; } - inline const_iterator &operator+=(int j) { if (j < 0) return (*this -= (-j)); while (j--) ++*this; return *this; } - inline const_iterator &operator-=(int j) { if (j < 0) return (*this += (-j)); while (j--) --*this; return *this; } - - private: - Row *_p; - friend class List; - - }; - friend class const_iterator; + using const_iterator = std::vector>::const_iterator; using iterator = const_iterator; - const_iterator cbegin() const { return const_iterator(_begin); } - const_iterator cend() const { return const_iterator(_end); } + const_iterator cbegin() const { return _rows.cbegin(); } + const_iterator cend() const { return _rows.cend(); } const_iterator begin() const { return cbegin(); } const_iterator end() const { return cend(); } - iterator begin() { return iterator(_begin); } - iterator end() { return iterator(_end); } - const_iterator cfind(Row *value) const { return value ? const_iterator(value) : cend(); } + iterator begin() { return cbegin(); } + iterator end() { return cend(); } + const_iterator cfind(Row *value) const; const_iterator find(Row *value) const { return cfind(value); } - iterator find(Row *value) { return value ? iterator(value) : end(); } + iterator find(Row *value) { return cfind(value); } const_iterator cfind(int y, int h) const { - adjustCurrent(y, h); - return iterator(_current); + const auto index = std::max(y, 0) / h; + return _rows.begin() + std::min(index, size()); } const_iterator find(int y, int h) const { return cfind(y, h); } - iterator find(int y, int h) { - adjustCurrent(y, h); - return iterator(_current); - } - - ~List(); + iterator find(int y, int h) { return cfind(y, h); } private: - void adjustCurrent(int y, int h) const; - bool insertBefore(Row *row, Row *before); - bool insertAfter(Row *row, Row *after); - static Row *next(Row *row) { - return row->_next; - } - static Row *prev(Row *row) { - return row->_prev; - } + void adjustByName(not_null row); + void rotate( + std::vector>::iterator first, + std::vector>::iterator middle, + std::vector>::iterator last); - std::unique_ptr _last; - Row *_begin; - Row *_end; - SortMode _sortMode; - int _count = 0; - - std::map> _rowByKey; - - mutable Row *_current; // cache + SortMode _sortMode = SortMode(); + std::vector> _rows; + std::map> _rowByKey; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index 6581b1db27..1767a71e19 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -42,11 +42,7 @@ class Row : public RippleRow { public: explicit Row(std::nullptr_t) { } - Row(Key key, Row *prev, Row *next, int pos) - : _id(key) - , _prev(prev) - , _next(next) - , _pos(pos) { + Row(Key key, int pos) : _id(key), _pos(pos) { } Key key() const { @@ -73,8 +69,6 @@ private: friend class List; Key _id; - Row *_prev = nullptr; - Row *_next = nullptr; int _pos = 0; };