dialogs_indexed_list.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "dialogs/dialogs_indexed_list.h"
  8. #include "main/main_session.h"
  9. #include "data/data_session.h"
  10. #include "history/history.h"
  11. namespace Dialogs {
  12. IndexedList::IndexedList(SortMode sortMode, FilterId filterId)
  13. : _sortMode(sortMode)
  14. , _filterId(filterId)
  15. , _list(sortMode, filterId)
  16. , _empty(sortMode, filterId) {
  17. }
  18. RowsByLetter IndexedList::addToEnd(Key key) {
  19. if (const auto row = _list.getRow(key)) {
  20. return { row };
  21. }
  22. auto result = RowsByLetter{ _list.addToEnd(key) };
  23. for (const auto &ch : key.entry()->chatListFirstLetters()) {
  24. auto j = _index.find(ch);
  25. if (j == _index.cend()) {
  26. j = _index.emplace(ch, _sortMode, _filterId).first;
  27. }
  28. result.letters.emplace(ch, j->second.addToEnd(key));
  29. }
  30. return result;
  31. }
  32. Row *IndexedList::addByName(Key key) {
  33. if (const auto row = _list.getRow(key)) {
  34. return row;
  35. }
  36. const auto result = _list.addByName(key);
  37. for (const auto &ch : key.entry()->chatListFirstLetters()) {
  38. auto j = _index.find(ch);
  39. if (j == _index.cend()) {
  40. j = _index.emplace(ch, _sortMode, _filterId).first;
  41. }
  42. j->second.addByName(key);
  43. }
  44. return result;
  45. }
  46. void IndexedList::adjustByDate(const RowsByLetter &links) {
  47. _list.adjustByDate(links.main);
  48. for (const auto &[ch, row] : links.letters) {
  49. if (auto it = _index.find(ch); it != _index.cend()) {
  50. it->second.adjustByDate(row);
  51. }
  52. }
  53. }
  54. bool IndexedList::updateHeights(float64 narrowRatio) {
  55. return _list.updateHeights(narrowRatio);
  56. }
  57. bool IndexedList::updateHeight(Key key, float64 narrowRatio) {
  58. return _list.updateHeight(key, narrowRatio);
  59. }
  60. void IndexedList::moveToTop(Key key) {
  61. if (_list.moveToTop(key)) {
  62. for (const auto &ch : key.entry()->chatListFirstLetters()) {
  63. if (auto it = _index.find(ch); it != _index.cend()) {
  64. it->second.moveToTop(key);
  65. }
  66. }
  67. }
  68. }
  69. void IndexedList::movePinned(Row *row, int deltaSign) {
  70. auto swapPinnedIndexWith = find(row);
  71. Assert(swapPinnedIndexWith != cend());
  72. if (deltaSign > 0) {
  73. ++swapPinnedIndexWith;
  74. } else {
  75. Assert(swapPinnedIndexWith != cbegin());
  76. --swapPinnedIndexWith;
  77. }
  78. row->key().entry()->owner().reorderTwoPinnedChats(
  79. _filterId,
  80. row->key(),
  81. (*swapPinnedIndexWith)->key());
  82. }
  83. void IndexedList::peerNameChanged(
  84. not_null<PeerData*> peer,
  85. const base::flat_set<QChar> &oldLetters) {
  86. Expects(_sortMode != SortMode::Date);
  87. if (const auto history = peer->owner().historyLoaded(peer)) {
  88. if (_sortMode == SortMode::Name) {
  89. adjustByName(history, oldLetters);
  90. } else {
  91. adjustNames(FilterId(), history, oldLetters);
  92. }
  93. }
  94. }
  95. void IndexedList::peerNameChanged(
  96. FilterId filterId,
  97. not_null<PeerData*> peer,
  98. const base::flat_set<QChar> &oldLetters) {
  99. Expects(_sortMode == SortMode::Date);
  100. if (const auto history = peer->owner().historyLoaded(peer)) {
  101. adjustNames(filterId, history, oldLetters);
  102. }
  103. }
  104. void IndexedList::adjustByName(
  105. Key key,
  106. const base::flat_set<QChar> &oldLetters) {
  107. Expects(_sortMode == SortMode::Name);
  108. const auto mainRow = _list.adjustByName(key);
  109. if (!mainRow) return;
  110. auto toRemove = oldLetters;
  111. auto toAdd = base::flat_set<QChar>();
  112. for (const auto &ch : key.entry()->chatListFirstLetters()) {
  113. auto j = toRemove.find(ch);
  114. if (j == toRemove.cend()) {
  115. toAdd.insert(ch);
  116. } else {
  117. toRemove.erase(j);
  118. if (auto it = _index.find(ch); it != _index.cend()) {
  119. it->second.adjustByName(key);
  120. }
  121. }
  122. }
  123. for (auto ch : toRemove) {
  124. if (auto it = _index.find(ch); it != _index.cend()) {
  125. it->second.remove(key, mainRow);
  126. }
  127. }
  128. if (!toAdd.empty()) {
  129. for (auto ch : toAdd) {
  130. auto j = _index.find(ch);
  131. if (j == _index.cend()) {
  132. j = _index.emplace(ch, _sortMode, _filterId).first;
  133. }
  134. j->second.addByName(key);
  135. }
  136. }
  137. }
  138. void IndexedList::adjustNames(
  139. FilterId filterId,
  140. not_null<History*> history,
  141. const base::flat_set<QChar> &oldLetters) {
  142. const auto key = Dialogs::Key(history);
  143. auto mainRow = _list.getRow(key);
  144. if (!mainRow) return;
  145. auto toRemove = oldLetters;
  146. auto toAdd = base::flat_set<QChar>();
  147. for (const auto &ch : key.entry()->chatListFirstLetters()) {
  148. auto j = toRemove.find(ch);
  149. if (j == toRemove.cend()) {
  150. toAdd.insert(ch);
  151. } else {
  152. toRemove.erase(j);
  153. }
  154. }
  155. for (auto ch : toRemove) {
  156. if (_sortMode == SortMode::Date) {
  157. history->removeChatListEntryByLetter(filterId, ch);
  158. }
  159. if (auto it = _index.find(ch); it != _index.cend()) {
  160. it->second.remove(key, mainRow);
  161. }
  162. }
  163. for (auto ch : toAdd) {
  164. auto j = _index.find(ch);
  165. if (j == _index.cend()) {
  166. j = _index.emplace(ch, _sortMode, _filterId).first;
  167. }
  168. auto row = j->second.addToEnd(key);
  169. if (_sortMode == SortMode::Date) {
  170. history->addChatListEntryByLetter(filterId, ch, row);
  171. }
  172. }
  173. }
  174. void IndexedList::remove(Key key, Row *replacedBy) {
  175. if (_list.remove(key, replacedBy)) {
  176. for (const auto &ch : key.entry()->chatListFirstLetters()) {
  177. if (const auto it = _index.find(ch); it != _index.cend()) {
  178. it->second.remove(key, replacedBy);
  179. }
  180. }
  181. }
  182. }
  183. void IndexedList::clear() {
  184. _list.clear();
  185. _index.clear();
  186. }
  187. std::vector<not_null<Row*>> IndexedList::filtered(
  188. const QStringList &words) const {
  189. const auto minimal = [&]() -> const Dialogs::List* {
  190. if (empty()) {
  191. return nullptr;
  192. }
  193. auto result = (const Dialogs::List*)nullptr;
  194. for (const auto &word : words) {
  195. if (word.isEmpty()) {
  196. continue;
  197. }
  198. const auto found = filtered(word[0]);
  199. if (!found || found->empty()) {
  200. return nullptr;
  201. } else if (!result || result->size() > found->size()) {
  202. result = found;
  203. }
  204. }
  205. return result;
  206. }();
  207. auto result = std::vector<not_null<Row*>>();
  208. if (!minimal || minimal->empty()) {
  209. return result;
  210. }
  211. result.reserve(minimal->size());
  212. for (const auto &row : *minimal) {
  213. const auto &nameWords = row->entry()->chatListNameWords();
  214. const auto found = [&](const QString &word) {
  215. for (const auto &name : nameWords) {
  216. if (name.startsWith(word)) {
  217. return true;
  218. }
  219. }
  220. return false;
  221. };
  222. const auto allFound = [&] {
  223. for (const auto &word : words) {
  224. if (!found(word)) {
  225. return false;
  226. }
  227. }
  228. return true;
  229. }();
  230. if (allFound) {
  231. result.push_back(row);
  232. }
  233. }
  234. return result;
  235. }
  236. } // namespace Dialogs