diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index b35c115fe..31ff4de2e 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -19,19 +19,18 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "lang.h" +#include "boxes/contactsbox.h" -#include "addcontactbox.h" -#include "contactsbox.h" +#include "dialogs/dialogs_indexed_list.h" +#include "lang.h" +#include "boxes/addcontactbox.h" +#include "boxes/contactsbox.h" #include "mainwidget.h" #include "window.h" - #include "application.h" - #include "ui/filedialog.h" -#include "photocropbox.h" - -#include "confirmbox.h" +#include "boxes/photocropbox.h" +#include "boxes/confirmbox.h" QString cantInviteError() { return lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.me/spambot"), lang(lng_cant_more_info))); @@ -40,53 +39,22 @@ QString cantInviteError() { ContactsInner::ContactsInner(CreatingGroupType creating) : TWidget() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _newItemHeight(creating == CreatingGroupNone ? st::contactsNewItemHeight : 0) -, _newItemSel(false) -, _chat(0) -, _channel(0) -, _membersFilter(MembersFilterRecent) -, _bot(0) , _creating(creating) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) -, _addToPeer(0) -, _addAdmin(0) -, _addAdminRequestId(0) -, _addAdminBox(0) -, _contacts(&App::main()->contactsList()) -, _sel(0) -, _filteredSel(-1) -, _mouseSel(false) -, _selCount(0) -, _searching(false) -, _byUsernameSel(-1) -, _addContactLnk(this, lang(lng_add_contact_button)) -, _saving(false) { +, _contacts(App::main()->contactsList()) +, _addContactLnk(this, lang(lng_add_contact_button)) { init(); } ContactsInner::ContactsInner(ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : TWidget() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _newItemHeight(0) -, _newItemSel(false) -, _chat(0) , _channel(channel) , _membersFilter(membersFilter) -, _bot(0) , _creating(CreatingGroupChannel) , _already(already) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) -, _addToPeer(0) -, _addAdmin(0) -, _addAdminRequestId(0) -, _addAdminBox(0) -, _contacts(&App::main()->contactsList()) -, _sel(0) -, _filteredSel(-1) -, _mouseSel(false) -, _selCount(0) -, _searching(false) -, _byUsernameSel(-1) -, _addContactLnk(this, lang(lng_add_contact_button)) -, _saving(false) { +, _contacts(App::main()->contactsList()) +, _addContactLnk(this, lang(lng_add_contact_button)) { init(); } @@ -98,34 +66,19 @@ namespace { ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWidget() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _newItemHeight(0) -, _newItemSel(false) , _chat(chat) -, _channel(0) , _membersFilter(membersFilter) -, _bot(0) -, _creating(CreatingGroupNone) , _allAdmins(this, lang(lng_chat_all_members_admins), !_chat->adminsEnabled(), st::contactsAdminCheckbox) , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right() - st::contactsCheckPosition.x() * 2 - st::contactsCheckIcon.pxWidth()) , _aboutAllAdmins(st::boxTextFont, lang(lng_chat_about_all_admins), _defaultOptions, _aboutWidth) , _aboutAdmins(st::boxTextFont, lang(lng_chat_about_admins), _defaultOptions, _aboutWidth) -, _addToPeer(0) -, _addAdmin(0) -, _addAdminRequestId(0) -, _addAdminBox(0) -, _contacts((membersFilter == MembersFilterRecent) ? (&App::main()->contactsList()) : (new DialogsIndexed(DialogsSortByAdd))) -, _sel(0) -, _filteredSel(-1) -, _mouseSel(false) -, _selCount(0) -, _searching(false) -, _byUsernameSel(-1) -, _addContactLnk(this, lang(lng_add_contact_button)) -, _saving(false) { +, _customList((membersFilter == MembersFilterRecent) ? UniquePointer() : MakeUnique(Dialogs::SortMode::Add)) +, _contacts((membersFilter == MembersFilterRecent) ? App::main()->contactsList() : _customList.data()) +, _addContactLnk(this, lang(lng_add_contact_button)) { initList(); if (membersFilter == MembersFilterAdmins) { _newItemHeight = st::contactsNewItemHeight + qMax(_aboutAllAdmins.countHeight(_aboutWidth), _aboutAdmins.countHeight(_aboutWidth)) + st::contactsAboutHeight; - if (!_contacts->list.count) { + if (_contacts->isEmpty()) { App::api()->requestFullPeer(_chat); } } @@ -134,33 +87,18 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid ContactsInner::ContactsInner(UserData *bot) : TWidget() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _newItemHeight(0) -, _newItemSel(false) -, _chat(0) -, _channel(0) -, _membersFilter(MembersFilterRecent) , _bot(bot) -, _creating(CreatingGroupNone) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) -, _addToPeer(0) -, _addAdmin(0) -, _addAdminRequestId(0) -, _addAdminBox(0) -, _contacts(new DialogsIndexed(DialogsSortByAdd)) -, _sel(0) -, _filteredSel(-1) -, _mouseSel(false) -, _selCount(0) -, _searching(false) -, _byUsernameSel(-1) -, _addContactLnk(this, lang(lng_add_contact_button)) -, _saving(false) { - DialogsIndexed &v(App::main()->dialogsList()); - for (DialogRow *r = v.list.begin; r != v.list.end; r = r->next) { - if (r->history->peer->isChat() && r->history->peer->asChat()->canEdit()) { - _contacts->addToEnd(r->history); - } else if (r->history->peer->isMegagroup() && (r->history->peer->asChannel()->amCreator() || r->history->peer->asChannel()->amEditor())) { - _contacts->addToEnd(r->history); +, _customList(MakeUnique(Dialogs::SortMode::Add)) +, _contacts(_customList.data()) +, _addContactLnk(this, lang(lng_add_contact_button)) { + auto v = App::main()->dialogsList(); + for_const (auto row, *v) { + auto peer = row->history()->peer; + if (peer->isChat() && peer->asChat()->canEdit()) { + _contacts->addToEnd(row->history()); + } else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) { + _contacts->addToEnd(row->history()); } } init(); @@ -173,14 +111,14 @@ void ContactsInner::init() { setAttribute(Qt::WA_OpaquePaintEvent); - for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) { - r->attached = 0; + for_const (auto row, _contacts->all()) { + row->attached = nullptr; } _filter = qsl("a"); updateFilter(); - connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow*,DialogRow*)), this, SLOT(onDialogRowReplaced(DialogRow*,DialogRow*))); + connect(App::main(), SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*))); connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData *))); connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&))); connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); @@ -305,7 +243,7 @@ void ContactsInner::saving(bool flag) { void ContactsInner::peerUpdated(PeerData *peer) { if (_chat && (!peer || peer == _chat)) { bool inited = false; - if (_membersFilter == MembersFilterAdmins && !_contacts->list.count && !_chat->participants.isEmpty()) { + if (_membersFilter == MembersFilterAdmins && _contacts->isEmpty() && !_chat->participants.isEmpty()) { initList(); inited = true; } @@ -316,8 +254,8 @@ void ContactsInner::peerUpdated(PeerData *peer) { delete i.value(); } _contactsData.clear(); - for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { - row->attached = 0; + for_const (auto row, _contacts->all()) { + row->attached = nullptr; } if (!_filter.isEmpty()) { for (int32 j = 0, s = _filtered.size(); j < s; ++j) { @@ -333,10 +271,10 @@ void ContactsInner::peerUpdated(PeerData *peer) { } else { ContactsData::iterator i = _contactsData.find(peer); if (i != _contactsData.cend()) { - for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { + for_const (auto row, _contacts->all()) { if (row->attached == i.value()) { - row->attached = 0; - update(0, _newItemHeight + _rowHeight * row->pos, width(), _rowHeight); + row->attached = nullptr; + update(0, _newItemHeight + _rowHeight * row->pos(), width(), _rowHeight); } } if (!_filter.isEmpty()) { @@ -361,14 +299,13 @@ void ContactsInner::loadProfilePhotos(int32 yFrom) { if (yFrom < 0) yFrom = 0; if (_filter.isEmpty()) { - if (_contacts->list.count) { - _contacts->list.adjustCurrent(yFrom - _newItemHeight, _rowHeight); - for ( - DialogRow *preloadFrom = _contacts->list.current; - preloadFrom != _contacts->list.end && (_newItemHeight + preloadFrom->pos * _rowHeight) < yTo; - preloadFrom = preloadFrom->next - ) { - preloadFrom->history->peer->loadUserpic(); + if (!_contacts->isEmpty()) { + auto i = _contacts->cfind(yFrom - _newItemHeight, _rowHeight); + for (auto end = _contacts->cend(); i != end; ++i) { + if ((_newItemHeight + (*i)->pos() * _rowHeight) >= yTo) { + break; + } + (*i)->history()->peer->loadUserpic(); } } } else if (!_filtered.isEmpty()) { @@ -379,16 +316,16 @@ void ContactsInner::loadProfilePhotos(int32 yFrom) { if (to > _filtered.size()) to = _filtered.size(); for (; from < to; ++from) { - _filtered[from]->history->peer->loadUserpic(); + _filtered[from]->history()->peer->loadUserpic(); } } } } -ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) { +ContactsInner::ContactData *ContactsInner::contactData(Dialogs::Row *row) { ContactData *data = (ContactData*)row->attached; if (!data) { - PeerData *peer = row->history->peer; + PeerData *peer = row->history()->peer; ContactsData::const_iterator i = _contactsData.constFind(peer); if (i == _contactsData.cend()) { _contactsData.insert(peer, data = new ContactData()); @@ -513,7 +450,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) { int32 yFrom = r.y(), yTo = r.y() + r.height(); if (_filter.isEmpty()) { - if (_contacts->list.count || !_byUsername.isEmpty()) { + if (!_contacts->isEmpty() || !_byUsername.isEmpty()) { if (_newItemHeight) { if (_chat) { p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); @@ -534,18 +471,18 @@ void ContactsInner::paintEvent(QPaintEvent *e) { yTo -= _newItemHeight; p.translate(0, _newItemHeight); } - if (_contacts->list.count) { - _contacts->list.adjustCurrent(yFrom, _rowHeight); - - DialogRow *drawFrom = _contacts->list.current; - p.translate(0, drawFrom->pos * _rowHeight); - while (drawFrom != _contacts->list.end && drawFrom->pos * _rowHeight < yTo) { - paintDialog(p, drawFrom->history->peer, contactData(drawFrom), (drawFrom == _sel)); + if (!_contacts->isEmpty()) { + auto i = _contacts->cfind(yFrom, _rowHeight); + p.translate(0, (*i)->pos() * _rowHeight); + for (auto end = _contacts->cend(); i != end; ++i) { + if ((*i)->pos() * _rowHeight >= yTo) { + break; + } + paintDialog(p, (*i)->history()->peer, contactData(*i), (*i == _sel)); p.translate(0, _rowHeight); - drawFrom = drawFrom->next; } - yFrom -= _contacts->list.count * _rowHeight; - yTo -= _contacts->list.count * _rowHeight; + yFrom -= _contacts->size() * _rowHeight; + yTo -= _contacts->size() * _rowHeight; } if (!_byUsername.isEmpty()) { p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b); @@ -609,7 +546,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) { int32 to = ceilclamp(yTo, _rowHeight, 0, _filtered.size()); p.translate(0, from * _rowHeight); for (; from < to; ++from) { - paintDialog(p, _filtered[from]->history->peer, contactData(_filtered[from]), (_filteredSel == from)); + paintDialog(p, _filtered[from]->history()->peer, contactData(_filtered[from]), (_filteredSel == from)); p.translate(0, _rowHeight); } } @@ -644,10 +581,10 @@ void ContactsInner::updateSelectedRow() { update(0, 0, width(), st::contactsNewItemHeight); } if (_sel) { - update(0, _newItemHeight + _sel->pos * _rowHeight, width(), _rowHeight); + update(0, _newItemHeight + _sel->pos() * _rowHeight, width(), _rowHeight); } if (_byUsernameSel >= 0) { - update(0, _newItemHeight + _contacts->list.count * _rowHeight + st::searchedBarHeight + _byUsernameSel * _rowHeight, width(), _rowHeight); + update(0, _newItemHeight + _contacts->size() * _rowHeight + st::searchedBarHeight + _byUsernameSel * _rowHeight, width(), _rowHeight); } } else { if (_filteredSel >= 0) { @@ -738,14 +675,14 @@ void ContactsInner::chooseParticipant() { if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) { peer = _byUsername[_byUsernameSel]; } else if (_sel) { - peer = _sel->history->peer; + peer = _sel->history()->peer; } } else { if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) { peer = _byUsernameFiltered[_byUsernameSel]; } else { if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return; - peer = _filtered[_filteredSel]->history->peer; + peer = _filtered[_filteredSel]->history()->peer; } } if (peer) { @@ -775,8 +712,8 @@ void ContactsInner::chooseParticipant() { update(); } -void ContactsInner::changeCheckState(DialogRow *row) { - changeCheckState(contactData(row), row->history->peer); +void ContactsInner::changeCheckState(Dialogs::Row *row) { + changeCheckState(contactData(row), row->history()->peer); } void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) { @@ -822,8 +759,8 @@ void ContactsInner::updateSel() { } p.setY(p.y() - _newItemHeight); } - DialogRow *newSel = (in && !newItemSel && (p.y() >= 0) && (p.y() < _contacts->list.count * _rowHeight)) ? _contacts->list.rowAtY(p.y(), _rowHeight) : 0; - int32 byUsernameSel = (in && !newItemSel && p.y() >= _contacts->list.count * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->list.count * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; + Dialogs::Row *newSel = (in && !newItemSel && (p.y() >= 0) && (p.y() < _contacts->size() * _rowHeight)) ? _contacts->rowAtY(p.y(), _rowHeight) : nullptr; + int32 byUsernameSel = (in && !newItemSel && p.y() >= _contacts->size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; if (byUsernameSel >= _byUsername.size()) byUsernameSel = -1; if (newSel != _sel || byUsernameSel != _byUsernameSel || newItemSel != _newItemSel) { updateSelectedRow(); @@ -883,23 +820,23 @@ void ContactsInner::updateFilter(QString filter) { _filtered.clear(); if (!f.isEmpty()) { - DialogsList *dialogsToFilter = 0; - if (_contacts->list.count) { + const Dialogs::List *toFilter = nullptr; + if (!_contacts->isEmpty()) { for (fi = fb; fi != fe; ++fi) { - DialogsIndexed::DialogsIndex::iterator i = _contacts->index.find(fi->at(0)); - if (i == _contacts->index.cend()) { - dialogsToFilter = 0; + auto found = _contacts->filtered(fi->at(0)); + if (found->isEmpty()) { + toFilter = nullptr; break; } - if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) { - dialogsToFilter = i.value(); + if (!toFilter || toFilter->size() > found->size()) { + toFilter = found; } } } - if (dialogsToFilter && dialogsToFilter->count) { - _filtered.reserve(dialogsToFilter->count); - for (DialogRow *i = dialogsToFilter->begin, *e = dialogsToFilter->end; i != e; i = i->next) { - const PeerData::Names &names(i->history->peer->names); + if (toFilter) { + _filtered.reserve(toFilter->size()); + for_const (auto row, *toFilter) { + const PeerData::Names &names(row->history()->peer->names); PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; for (fi = fb; fi != fe; ++fi) { QString filterName(*fi); @@ -913,8 +850,8 @@ void ContactsInner::updateFilter(QString filter) { } } if (fi == fe) { - i->attached = 0; - _filtered.push_back(i); + row->attached = nullptr; + _filtered.push_back(row); } } } @@ -968,7 +905,7 @@ void ContactsInner::updateFilter(QString filter) { } } -void ContactsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) { +void ContactsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { if (!_filter.isEmpty()) { for (FilteredDialogs::iterator i = _filtered.begin(), e = _filtered.end(); i != e;) { if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts! @@ -991,8 +928,8 @@ void ContactsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) { } } _mouseSel = false; - int32 cnt = (_filter.isEmpty() ? _contacts->list.count : _filtered.size()); - int32 newh = cnt ? (cnt * _rowHeight) : st::noContactsHeight; + int cnt = (_filter.isEmpty() ? _contacts->size() : _filtered.size()); + int newh = cnt ? (cnt * _rowHeight) : st::noContactsHeight; resize(width(), newh); } @@ -1050,9 +987,9 @@ void ContactsInner::refresh() { } else { if (!_allAdmins.isHidden()) _allAdmins.hide(); } - if (_contacts->list.count || !_byUsername.isEmpty()) { + if (!_contacts->isEmpty() || !_byUsername.isEmpty()) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); - resize(width(), _newItemHeight + (_contacts->list.count * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight))); + resize(width(), _newItemHeight + (_contacts->size() * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight))); } else if (_chat && _membersFilter == MembersFilterAdmins) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); resize(width(), _newItemHeight + st::noContactsHeight); @@ -1101,7 +1038,6 @@ ContactsInner::~ContactsInner() { delete *i; } if (_bot || (_chat && _membersFilter == MembersFilterAdmins)) { - delete _contacts; if (_bot && _bot->botInfo) _bot->botInfo->startGroupToken = QString(); } } @@ -1117,12 +1053,12 @@ void ContactsInner::selectSkip(int32 dir) { if (_filter.isEmpty()) { int cur = 0; if (_sel) { - for (DialogRow *i = _contacts->list.begin; i != _sel; i = i->next) { + for (auto i = _contacts->cbegin(); *i != _sel; ++i) { ++cur; } if (_newItemHeight) ++cur; } else if (_byUsernameSel >= 0) { - cur = (_contacts->list.count + _byUsernameSel); + cur = (_contacts->size() + _byUsernameSel); if (_newItemHeight) ++cur; } else if (!_newItemSel) { cur = -1; @@ -1130,32 +1066,39 @@ void ContactsInner::selectSkip(int32 dir) { cur += dir; if (cur <= 0) { _newItemSel = (_chat && _membersFilter == MembersFilterAdmins) ? false : (_newItemHeight ? true : false); - _sel = (!_newItemHeight && _contacts->list.count) ? _contacts->list.begin : 0; - _byUsernameSel = (!_newItemHeight && !_contacts->list.count && !_byUsername.isEmpty()) ? 0 : -1; - } else if (cur >= _contacts->list.count + (_newItemHeight ? 1 : 0)) { + _sel = (!_newItemHeight && !_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr; + _byUsernameSel = (!_newItemHeight && _contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1; + } else if (cur >= _contacts->size() + (_newItemHeight ? 1 : 0)) { _newItemSel = false; if (_byUsername.isEmpty()) { - _sel = _contacts->list.count ? _contacts->list.end->prev : 0; + _sel = _contacts->isEmpty() ? nullptr : *(_contacts->cend() - 1); _byUsernameSel = -1; } else { - _sel = 0; - _byUsernameSel = cur - _contacts->list.count; + _sel = nullptr; + _byUsernameSel = cur - _contacts->size(); if (_byUsernameSel >= _byUsername.size()) _byUsernameSel = _byUsername.size() - 1; } } else { _newItemSel = false; if (_newItemHeight) --cur; - for (_sel = _contacts->list.begin; cur; _sel = _sel->next) { - --cur; + for (auto i = _contacts->cbegin(); ; ++i) { + _sel = *i; + if (!cur) { + break; + } else { + --cur; + } } _byUsernameSel = -1; } if (dir > 0) { - while (_sel && _sel->next && contactData(_sel)->inchat) { - _sel = _sel->next; + for (auto i = _contacts->cfind(_sel), end = _contacts->cend(); i != end && contactData(*i)->inchat; ++i) { + _sel = *i; } - if (!_sel || !_sel->next) { - _sel = 0; + if (contactData(_sel)->inchat) { + _sel = nullptr; + } + if (!_sel) { if (!_byUsername.isEmpty()) { if (_byUsernameSel < 0) _byUsernameSel = 0; for (; _byUsernameSel < _byUsername.size() && d_byUsername[_byUsernameSel]->inchat;) { @@ -1169,10 +1112,13 @@ void ContactsInner::selectSkip(int32 dir) { --_byUsernameSel; } if (_byUsernameSel < 0) { - if (_contacts->list.count) { - if (!_newItemSel && !_sel) _sel = _contacts->list.end->prev; - for (; _sel && contactData(_sel)->inchat;) { - _sel = _sel->prev; + if (!_contacts->isEmpty()) { + if (!_newItemSel && !_sel) _sel = *(_contacts->cend() - 1); + for (auto i = _contacts->cfind(_sel), b = _contacts->cbegin(); i != b && contactData(*i)->inchat; --i) { + _sel = *i; + } + if (contactData(_sel)->inchat) { + _sel = nullptr; } } } @@ -1180,9 +1126,9 @@ void ContactsInner::selectSkip(int32 dir) { if (_newItemSel) { emit mustScrollTo(0, _newItemHeight); } else if (_sel) { - emit mustScrollTo(_newItemHeight + _sel->pos * _rowHeight, _newItemHeight + (_sel->pos + 1) * _rowHeight); + emit mustScrollTo(_newItemHeight + _sel->pos() * _rowHeight, _newItemHeight + (_sel->pos() + 1) * _rowHeight); } else if (_byUsernameSel >= 0) { - emit mustScrollTo(_newItemHeight + (_contacts->list.count + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _newItemHeight + (_contacts->list.count + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight); + emit mustScrollTo(_newItemHeight + (_contacts->size() + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _newItemHeight + (_contacts->size() + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight); } } else { int cur = (_filteredSel >= 0) ? _filteredSel : ((_byUsernameSel >= 0) ? (_filtered.size() + _byUsernameSel) : -1); @@ -1243,8 +1189,8 @@ void ContactsInner::selectSkipPage(int32 h, int32 dir) { QVector ContactsInner::selected() { QVector result; - for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { - if (_checkedContacts.contains(row->history->peer)) { + for_const (auto row, *_contacts) { + if (_checkedContacts.contains(row->history()->peer)) { contactData(row); // fill _contactsData } } @@ -1264,8 +1210,8 @@ QVector ContactsInner::selected() { QVector ContactsInner::selectedInputs() { QVector result; - for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { - if (_checkedContacts.contains(row->history->peer)) { + for_const (auto row, *_contacts) { + if (_checkedContacts.contains(row->history()->peer)) { contactData(row); // fill _contactsData } } @@ -1284,8 +1230,8 @@ QVector ContactsInner::selectedInputs() { } PeerData *ContactsInner::selectedUser() { - for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { - if (_checkedContacts.contains(row->history->peer)) { + for_const (auto row, *_contacts) { + if (_checkedContacts.contains(row->history()->peer)) { contactData(row); // fill _contactsData } } diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 87d7ad807..fc3b3f8a6 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -22,6 +22,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Dialogs { +class Row; +class IndexedList; +} // namespace Dialogs + enum MembersFilter { MembersFilterRecent, MembersFilterAdmins, @@ -69,7 +74,7 @@ public: void loadProfilePhotos(int32 yFrom); void chooseParticipant(); - void changeCheckState(DialogRow *row); + void changeCheckState(Dialogs::Row *row); void changeCheckState(ContactData *data, PeerData *peer); void peopleReceived(const QString &query, const QVector &people); @@ -102,7 +107,7 @@ signals: public slots: - void onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow); + void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); void updateSel(); void peerUpdated(PeerData *peer); @@ -120,36 +125,38 @@ private: void addAdminDone(const MTPUpdates &result, mtpRequestId req); bool addAdminFail(const RPCError &error, mtpRequestId req); - int32 _rowHeight, _newItemHeight; - bool _newItemSel; + int32 _rowHeight; + int _newItemHeight = 0; + bool _newItemSel = false; - ChatData *_chat; - ChannelData *_channel; - MembersFilter _membersFilter; - UserData *_bot; - CreatingGroupType _creating; + ChatData *_chat = nullptr; + ChannelData *_channel = nullptr; + MembersFilter _membersFilter = MembersFilterRecent; + UserData *_bot = nullptr; + CreatingGroupType _creating = CreatingGroupNone; MembersAlreadyIn _already; Checkbox _allAdmins; int32 _aboutWidth; Text _aboutAllAdmins, _aboutAdmins; - PeerData *_addToPeer; - UserData *_addAdmin; - mtpRequestId _addAdminRequestId; - ConfirmBox *_addAdminBox; + PeerData *_addToPeer = nullptr; + UserData *_addAdmin = nullptr; + mtpRequestId _addAdminRequestId = 0; + ConfirmBox *_addAdminBox = nullptr; int32 _time; - DialogsIndexed *_contacts; - DialogRow *_sel; + UniquePointer _customList; + Dialogs::IndexedList *_contacts = nullptr; + Dialogs::Row *_sel = nullptr; QString _filter; - typedef QVector FilteredDialogs; + typedef QVector FilteredDialogs; FilteredDialogs _filtered; - int32 _filteredSel; - bool _mouseSel; + int _filteredSel = -1; + bool _mouseSel = false; - int32 _selCount; + int _selCount = 0; struct ContactData { Text name; @@ -163,22 +170,22 @@ private: typedef QMap CheckedContacts; CheckedContacts _checkedContacts; - ContactData *contactData(DialogRow *row); + ContactData *contactData(Dialogs::Row *row); - bool _searching; + bool _searching = false; QString _lastQuery; typedef QVector ByUsernameRows; typedef QVector ByUsernameDatas; ByUsernameRows _byUsername, _byUsernameFiltered; ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas ByUsernameDatas _byUsernameDatas; - int32 _byUsernameSel; + int _byUsernameSel = -1; QPoint _lastMousePos; LinkButton _addContactLnk; - bool _saving; - bool _allAdminsChecked; + bool _saving = false; + bool _allAdminsChecked = false; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs_common.h b/Telegram/SourceFiles/dialogs/dialogs_common.h new file mode 100644 index 000000000..a7f7de725 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_common.h @@ -0,0 +1,34 @@ +/* +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 +*/ +#pragma once + +namespace Dialogs { + +class Row; +using RowsByLetter = QMap; + +enum class SortMode { + Date, + Name, + Add +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp new file mode 100644 index 000000000..af6ef8ebe --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp @@ -0,0 +1,164 @@ +/* +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_indexed_list.h" + +namespace Dialogs { + +IndexedList::IndexedList(SortMode sortMode) +: _sortMode(sortMode) +, _list(sortMode) { +} + +RowsByLetter IndexedList::addToEnd(History *history) { + RowsByLetter result; + if (!_list.contains(history->peer->id)) { + result.insert(0, _list.addToEnd(history)); + for_const (auto ch, history->peer->chars) { + auto j = _index.find(ch); + if (j == _index.cend()) { + j = _index.insert(ch, new List(_sortMode)); + } + result.insert(ch, j.value()->addToEnd(history)); + } + } + return result; +} + +Row *IndexedList::addByName(History *history) { + if (auto row = _list.getRow(history->peer->id)) { + return row; + } + + Row *result = _list.addByName(history); + for_const (auto ch, history->peer->chars) { + auto j = _index.find(ch); + if (j == _index.cend()) { + j = _index.insert(ch, new List(_sortMode)); + } + j.value()->addByName(history); + } + return result; +} + +void IndexedList::adjustByPos(const RowsByLetter &links) { + for (auto i = links.cbegin(), e = links.cend(); i != e; ++i) { + if (i.key() == QChar(0)) { + _list.adjustByPos(i.value()); + } else { + if (auto list = _index.value(i.key())) { + list->adjustByPos(i.value()); + } + } + } +} + + +void IndexedList::peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { + if (_sortMode == SortMode::Name) { + Row *mainRow = _list.adjustByName(peer); + if (!mainRow) return; + + History *history = mainRow->history(); + + PeerData::NameFirstChars toRemove = oldChars, toAdd; + for_const (auto ch, peer->chars) { + auto j = toRemove.find(ch); + if (j == toRemove.cend()) { + toAdd.insert(ch); + } else { + toRemove.erase(j); + if (auto list = _index.value(ch)) { + list->adjustByName(peer); + } + } + } + for_const (auto ch, toRemove) { + if (auto list = _index.value(ch)) { + list->del(peer->id, mainRow); + } + } + if (!toAdd.isEmpty()) { + for_const (auto ch, toAdd) { + auto j = _index.find(ch); + if (j == _index.cend()) { + j = _index.insert(ch, new List(_sortMode)); + } + j.value()->addByName(history); + } + } + } else { + auto mainRow = _list.getRow(peer->id); + if (!mainRow) return; + + History *history = mainRow->history(); + + PeerData::NameFirstChars toRemove = oldChars, toAdd; + for_const (auto ch, peer->chars) { + auto j = toRemove.find(ch); + if (j == toRemove.cend()) { + toAdd.insert(ch); + } else { + toRemove.erase(j); + } + } + for_const (auto ch, toRemove) { + if (_sortMode == SortMode::Date) { + history->removeChatListEntryByLetter(ch); + } + if (auto list = _index.value(ch)) { + list->del(peer->id, mainRow); + } + } + for_const (auto ch, toAdd) { + auto j = _index.find(ch); + if (j == _index.cend()) { + j = _index.insert(ch, new List(_sortMode)); + } + Row *row = j.value()->addToEnd(history); + if (_sortMode == SortMode::Date) { + history->addChatListEntryByLetter(ch, row); + } + } + } +} + +void IndexedList::del(const PeerData *peer, Row *replacedBy) { + if (_list.del(peer->id, replacedBy)) { + for_const (auto ch, peer->chars) { + if (auto list = _index.value(ch)) { + list->del(peer->id, replacedBy); + } + } + } +} + +void IndexedList::clear() { + for_const (auto &list, _index) { + delete list; + } +} + +IndexedList::~IndexedList() { + clear(); +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h new file mode 100644 index 000000000..e0acfd0aa --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h @@ -0,0 +1,81 @@ +/* +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 +*/ +#pragma once + +#include "dialogs/dialogs_common.h" +#include "dialogs/dialogs_list.h" + +class History; + +namespace Dialogs { + +class IndexedList { +public: + IndexedList(SortMode sortMode); + + RowsByLetter addToEnd(History *history); + Row *addByName(History *history); + void adjustByPos(const RowsByLetter &links); + void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); + void del(const PeerData *peer, Row *replacedBy = nullptr); + void clear(); + + const List &all() const { + return _list; + } + const List *filtered(QChar ch) const { + static StaticNeverFreedPointer empty(new List(SortMode::Add)); + return _index.value(ch, empty.data()); + } + + ~IndexedList(); + + // Part of List interface is duplicated here for all() list. + int size() const { return all().size(); } + bool isEmpty() const { return all().isEmpty(); } + bool contains(PeerId peerId) const { return all().contains(peerId); } + Row *getRow(PeerId peerId) const { return all().getRow(peerId); } + Row *rowAtY(int32 y, int32 h) const { return all().rowAtY(y, h); } + + using iterator = List::iterator; + using const_iterator = List::const_iterator; + const_iterator cbegin() const { return all().cbegin(); } + const_iterator cend() const { return all().cend(); } + const_iterator begin() const { return all().cbegin(); } + const_iterator end() const { return all().cend(); } + iterator begin() { return all().begin(); } + iterator end() { return all().end(); } + const_iterator cfind(Row *value) const { return all().cfind(value); } + const_iterator find(Row *value) const { return all().cfind(value); } + iterator find(Row *value) { return all().find(value); } + const_iterator cfind(int y, int h) const { return all().cfind(y, h); } + const_iterator find(int y, int h) const { return all().cfind(y, h); } + iterator find(int y, int h) { return all().find(y, h); } + +private: + SortMode _sortMode; + List _list; + using Index = QMap; + Index _index; + +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp new file mode 100644 index 000000000..6963d923e --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -0,0 +1,205 @@ +/* +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_layout.h" + +#include "dialogs/dialogs_list.h" +#include "lang.h" + +namespace Dialogs { +namespace Layout { + +void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground) { + QRect fullRect(0, 0, w, st::dlgHeight); + p.fillRect(fullRect, active ? st::dlgActiveBG : (selected ? st::dlgHoverBG : st::dlgBG)); + if (onlyBackground) return; + + History *history = row->history(); + PeerData *userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); + userpicPeer->paintUserpicLeft(p, st::dlgPhotoSize, st::dlgPaddingHor, st::dlgPaddingVer, w); + + int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding; + int32 namewidth = w - nameleft - st::dlgPaddingHor; + QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height); + + // draw chat icon + if (history->peer->isChat() || history->peer->isMegagroup()) { + p.drawSprite(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), (active ? st::dlgActiveChatImg : st::dlgChatImg)); + rectForName.setLeft(rectForName.left() + st::dlgImgSkip); + } else if (history->peer->isChannel()) { + p.drawSprite(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), (active ? st::dlgActiveChannelImg : st::dlgChannelImg)); + rectForName.setLeft(rectForName.left() + st::dlgImgSkip); + } + + HistoryItem *last = history->lastMsg; + if (!last) { + p.setFont(st::dlgHistFont); + p.setPen(active ? st::dlgActiveColor : st::dlgSystemColor); + if (history->typing.isEmpty() && history->sendActions.isEmpty()) { + p.drawText(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgFont->ascent + st::dlgSep, lang(lng_empty_history)); + } else { + history->typingText.drawElided(p, nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth); + } + } else { + // draw date + QDateTime now(QDateTime::currentDateTime()), lastTime(last->date); + QDate nowDate(now.date()), lastDate(lastTime.date()); + QString dt; + if (lastDate == nowDate) { + dt = lastTime.toString(cTimeFormat()); + } else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) { + dt = langDayOfWeek(lastDate); + } else { + dt = lastDate.toString(qsl("d.MM.yy")); + } + int32 dtWidth = st::dlgDateFont->width(dt); + rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip); + p.setFont(st::dlgDateFont->f); + p.setPen((active ? st::dlgActiveDateColor : st::dlgDateColor)->p); + p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt); + + // draw check + if (last->needCheck()) { + const style::sprite *check; + if (last->id > 0) { + if (last->unread()) { + check = active ? &st::dlgActiveCheckImg : &st::dlgCheckImg; + } else { + check = active ? &st::dlgActiveDblCheckImg : &st::dlgDblCheckImg; + } + } else { + check = active ? &st::dlgActiveSendImg : &st::dlgSendImg; + } + rectForName.setWidth(rectForName.width() - check->pxWidth() - st::dlgCheckSkip); + p.drawPixmap(QPoint(rectForName.left() + rectForName.width() + st::dlgCheckLeft, rectForName.top() + st::dlgCheckTop), App::sprite(), *check); + } + + // draw unread + int32 lastWidth = namewidth, unread = history->unreadCount; + if (history->peer->migrateFrom()) { + if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) { + unread += h->unreadCount; + } + } + if (unread) { + QString unreadStr = QString::number(unread); + int32 unreadWidth = st::dlgUnreadFont->width(unreadStr); + int32 unreadRectWidth = unreadWidth + 2 * st::dlgUnreadPaddingHor; + int32 unreadRectHeight = st::dlgUnreadFont->height + 2 * st::dlgUnreadPaddingVer; + int32 unreadRectLeft = w - st::dlgPaddingHor - unreadRectWidth; + int32 unreadRectTop = st::dlgHeight - st::dlgPaddingVer - unreadRectHeight; + lastWidth -= unreadRectWidth + st::dlgUnreadPaddingHor; + p.setBrush(active ? (history->mute ? st::dlgActiveUnreadMutedBG : st::dlgActiveUnreadBG) : (history->mute ? st::dlgUnreadMutedBG : st::dlgUnreadBG)); + p.setPen(Qt::NoPen); + p.drawRoundedRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight, st::dlgUnreadRadius, st::dlgUnreadRadius); + p.setFont(st::dlgUnreadFont->f); + p.setPen(active ? st::dlgActiveUnreadColor : st::dlgUnreadColor); + p.drawText(unreadRectLeft + st::dlgUnreadPaddingHor, unreadRectTop + st::dlgUnreadPaddingVer + st::dlgUnreadFont->ascent, unreadStr); + } + if (history->typing.isEmpty() && history->sendActions.isEmpty()) { + last->drawInDialog(p, QRect(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth, st::dlgFont->height), active, history->textCachedFor, history->lastItemTextCache); + } else { + p.setPen(active ? st::dlgActiveColor : st::dlgSystemColor); + history->typingText.drawElided(p, nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth); + } + } + + if (history->peer->isUser() && history->peer->isVerified()) { + rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x()); + p.drawSprite(rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (active ? st::verifiedCheckInv : st::verifiedCheck)); + } + + p.setPen(active ? st::dlgActiveColor : st::dlgNameColor); + history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); +} + +void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground) { + QRect fullRect(0, 0, w, st::dlgHeight); + p.fillRect(fullRect, (active ? st::dlgActiveBG : (selected ? st::dlgHoverBG : st::dlgBG))->b); + if (onlyBackground) return; + + auto item = row->item(); + auto history = item->history(); + PeerData *userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); + userpicPeer->paintUserpicLeft(p, st::dlgPhotoSize, st::dlgPaddingHor, st::dlgPaddingVer, w); + + int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding; + int32 namewidth = w - nameleft - st::dlgPaddingHor; + QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height); + + // draw chat icon + if (history->peer->isChat() || history->peer->isMegagroup()) { + p.drawSprite(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), (active ? st::dlgActiveChatImg : st::dlgChatImg)); + rectForName.setLeft(rectForName.left() + st::dlgImgSkip); + } else if (history->peer->isChannel()) { + p.drawSprite(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), (active ? st::dlgActiveChannelImg : st::dlgChannelImg)); + rectForName.setLeft(rectForName.left() + st::dlgImgSkip); + } + + // draw date + QDateTime now(QDateTime::currentDateTime()), lastTime(item->date); + QDate nowDate(now.date()), lastDate(lastTime.date()); + QString dt; + if (lastDate == nowDate) { + dt = lastTime.toString(cTimeFormat()); + } else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) { + dt = langDayOfWeek(lastDate); + } else { + dt = lastDate.toString(qsl("d.MM.yy")); + } + int32 dtWidth = st::dlgDateFont->width(dt); + rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip); + p.setFont(st::dlgDateFont); + p.setPen(active ? st::dlgActiveDateColor : st::dlgDateColor); + p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt); + + // draw check + if (item->needCheck()) { + const style::sprite *check; + if (item->id > 0) { + if (item->unread()) { + check = active ? &st::dlgActiveCheckImg : &st::dlgCheckImg; + } else { + check = active ? &st::dlgActiveDblCheckImg : &st::dlgDblCheckImg; + } + } else { + check = active ? &st::dlgActiveSendImg : &st::dlgSendImg; + } + rectForName.setWidth(rectForName.width() - check->pxWidth() - st::dlgCheckSkip); + p.drawSprite(QPoint(rectForName.left() + rectForName.width() + st::dlgCheckLeft, rectForName.top() + st::dlgCheckTop), *check); + } + + // draw unread + int32 lastWidth = namewidth; + item->drawInDialog(p, QRect(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth, st::dlgFont->height), active, row->_cacheFor, row->_cache); + + if (history->peer->isUser() && history->peer->isVerified()) { + rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x()); + p.drawSprite(rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (active ? st::verifiedCheckInv : st::verifiedCheck)); + } + + p.setPen(active ? st::dlgActiveColor : st::dlgNameColor); + history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); + +} + +} // namespace Layout +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h new file mode 100644 index 000000000..de5d11813 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -0,0 +1,37 @@ +/* +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 +*/ +#pragma once + +namespace Dialogs { + +class Row; +class FakeRow; + +namespace Layout { + +class RowPainter { +public: + static void paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground); + static void paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground); +}; + +} // namespace Layout +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp new file mode 100644 index 000000000..f6e73d2d0 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp @@ -0,0 +1,239 @@ +/* +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 "mainwidget.h" + +namespace Dialogs { + +List::List(SortMode sortMode) +: _last(MakeUnique(nullptr, nullptr, nullptr, 0)) +, _begin(_last.data()) +, _end(_last.data()) +, _sortMode(sortMode) +, _current(_last.data()) { +} + +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::dlgHeight); + + Row *row = _current; + p.translate(0, row->_pos * st::dlgHeight); + while (row != _end && row->_pos * st::dlgHeight < 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::dlgHeight); + } +} + +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::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 diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.h b/Telegram/SourceFiles/dialogs/dialogs_list.h new file mode 100644 index 000000000..bd281d2f7 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_list.h @@ -0,0 +1,136 @@ +/* +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 +*/ +#pragma once + +#include "dialogs/dialogs_row.h" + +class PeerData; +namespace Dialogs { + +class List { +public: + List(SortMode sortMode); + List(const List &other) = delete; + List &operator=(const List &other) = delete; + + int size() const { + return _count; + } + bool isEmpty() const { + return size() == 0; + } + bool contains(PeerId peerId) const { + return _rowByPeer.contains(peerId); + } + Row *getRow(PeerId peerId) const { + return _rowByPeer.value(peerId); + } + Row *rowAtY(int32 y, int32 h) const { + auto i = cfind(y, h); + if (i == cend() || (*i)->pos() != ((y > 0) ? (y / h) : 0)) { + return nullptr; + } + return *i; + } + + void paint(Painter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel, bool onlyBackground) const; + Row *addToEnd(History *history); + bool insertBefore(Row *row, Row *before); + bool insertAfter(Row *row, Row *after); + Row *adjustByName(const PeerData *peer); + Row *addByName(History *history); + void adjustByPos(Row *row); + bool del(PeerId peerId, 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 iterator = const_iterator; + + const_iterator cbegin() const { return const_iterator(_begin); } + const_iterator cend() const { return const_iterator(_end); } + 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(); } + const_iterator find(Row *value) const { return cfind(value); } + iterator find(Row *value) { return value ? iterator(value) : end(); } + const_iterator cfind(int y, int h) const { + adjustCurrent(y, h); + return iterator(_current); + } + 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(); + +private: + void adjustCurrent(int y, int h) const; + static Row *next(Row *row) { + return row->_next; + } + static Row *prev(Row *row) { + return row->_prev; + } + + UniquePointer _last; + Row *_begin; + Row *_end; + SortMode _sortMode; + int _count = 0; + + typedef QHash RowByPeer; + RowByPeer _rowByPeer; + + mutable Row *_current; // cache +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h new file mode 100644 index 000000000..88dd3ca6f --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -0,0 +1,78 @@ +/* +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 +*/ +#pragma once + +#include "ui/text.h" + +class History; +class HistoryItem; + +namespace Dialogs { +namespace Layout { +class RowPainter; +} // namespace Layout + +class List; +class Row { +public: + Row(History *history, Row *prev, Row *next, int pos) + : _history(history) + , _prev(prev) + , _next(next) + , _pos(pos) { + } + void *attached = nullptr; // for any attached data, for example View in contacts list + + History *history() const { + return _history; + } + int pos() const { + return _pos; + } + +private: + friend class List; + + History *_history; + Row *_prev, *_next; + int _pos; + +}; + +class FakeRow { +public: + FakeRow(HistoryItem *item) : _item(item) { + } + + HistoryItem *item() const { + return _item; + } + +private: + friend class Layout::RowPainter; + + HistoryItem *_item; + mutable const HistoryItem *_cacheFor = nullptr; + mutable Text _cache = Text{ int(st::dlgRichMinWidth) }; + +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 08579c182..44e25e097 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -19,9 +19,12 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "dialogswidget.h" + +#include "dialogs/dialogs_indexed_list.h" +#include "dialogs/dialogs_layout.h" #include "ui/style.h" #include "lang.h" - #include "application.h" #include "window.h" #include "dialogswidget.h" @@ -29,39 +32,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/addcontactbox.h" #include "boxes/contactsbox.h" #include "boxes/confirmbox.h" - #include "localstorage.h" DialogsInner::DialogsInner(QWidget *parent, MainWidget *main) : SplittedWidget(parent) -, dialogs(DialogsSortByDate) -, contactsNoDialogs(DialogsSortByName) -, contacts(DialogsSortByName) -, sel(0) -, contactSel(false) -, selByMouse(false) -, _hashtagSel(-1) -, _filteredSel(-1) -, _searchedCount(0) -, _searchedMigratedCount(0) -, _searchedSel(-1) -, _peopleSel(-1) -, _lastSearchDate(0) -, _lastSearchPeer(0) -, _lastSearchId(0) -, _lastSearchMigratedId(0) -, _state(DefaultState) +, dialogs(MakeUnique(Dialogs::SortMode::Date)) +, contactsNoDialogs(MakeUnique(Dialogs::SortMode::Name)) +, contacts(MakeUnique(Dialogs::SortMode::Name)) , _addContactLnk(this, lang(lng_add_contact_button)) -, _cancelSearchInPeer(this, st::btnCancelSearch) -, _overDelete(false) -, _searchInPeer(0) -, _searchInMigrated(0) -, _menuPeer(0) -, _menuActionPeer(0) -, _menu(0) { +, _cancelSearchInPeer(this, st::btnCancelSearch) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); connect(main, SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*))); - connect(main, SIGNAL(dialogRowReplaced(DialogRow*,DialogRow*)), this, SLOT(onDialogRowReplaced(DialogRow*,DialogRow*))); + connect(main, SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*))); connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); connect(&_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer())); _cancelSearchInPeer.hide(); @@ -94,14 +76,12 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO } if (_state == DefaultState) { - int32 otherStart = dialogs.list.count * st::dlgHeight; - PeerData *active = App::main()->activePeer(), *selected = _menuPeer ? _menuPeer : (sel ? sel->history->peer : 0); + int32 otherStart = dialogs->size() * st::dlgHeight; + PeerData *active = App::main()->activePeer(), *selected = _menuPeer ? _menuPeer : (sel ? sel->history()->peer : 0); if (otherStart) { - dialogs.list.paint(p, fullWidth(), r.top(), r.top() + r.height(), active, selected, paintingOther); + dialogs->all().paint(p, fullWidth(), r.top(), r.top() + r.height(), active, selected, paintingOther); } - if (contactsNoDialogs.list.count && false) { - contactsNoDialogs.list.paint(p, fullWidth(), r.top() - otherStart, r.top() + r.height() - otherStart, active, selected, paintingOther); - } else if (!otherStart) { + if (!otherStart) { p.fillRect(r, st::white->b); if (!paintingOther) { p.setFont(st::noContactsFont->f); @@ -163,9 +143,9 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO PeerData *act = App::main()->activePeer(); MsgId actId = App::main()->activeMsgId(); for (; from < to; ++from) { - bool active = ((_filterResults[from]->history->peer == act) || (_filterResults[from]->history->peer->migrateTo() && _filterResults[from]->history->peer->migrateTo() == act)) && !actId; - bool selected = (from == _filteredSel) || (_filterResults[from]->history->peer == _menuPeer); - _filterResults[from]->paint(p, w, active, selected, paintingOther); + bool active = ((_filterResults[from]->history()->peer == act) || (_filterResults[from]->history()->peer->migrateTo() && _filterResults[from]->history()->peer->migrateTo() == act)) && !actId; + bool selected = (from == _filteredSel) || (_filterResults[from]->history()->peer == _menuPeer); + Dialogs::Layout::RowPainter::paint(p, _filterResults[from], w, active, selected, paintingOther); p.translate(0, st::dlgHeight); } } @@ -230,9 +210,12 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO PeerData *act = App::main()->activePeer(); MsgId actId = App::main()->activeMsgId(); for (; from < to; ++from) { - bool active = (_searchResults[from]->_item->history()->peer == act && _searchResults[from]->_item->id == actId) || (_searchResults[from]->_item->history()->peer->migrateTo() && _searchResults[from]->_item->history()->peer->migrateTo() == act && _searchResults[from]->_item->id == -actId); + auto result = _searchResults[from]; + auto item = result->item(); + auto history = item->history(); + bool active = (history->peer == act && item->id == actId) || (history->peer->migrateTo() && history->peer->migrateTo() == act && item->id == -actId); bool selected = (from == _searchedSel); - _searchResults[from]->paint(p, w, active, selected, paintingOther); + Dialogs::Layout::RowPainter::paint(p, result, w, active, selected, paintingOther); p.translate(0, st::dlgHeight); } } @@ -336,14 +319,8 @@ void DialogsInner::onUpdateSelected(bool force) { int w = width(), mouseY = mouse.y(); _overDelete = false; if (_state == DefaultState) { - DialogRow *newSel = dialogs.list.rowAtY(mouseY, st::dlgHeight); - int32 otherStart = dialogs.list.count * st::dlgHeight; - if (newSel) { - contactSel = false; - } else { - newSel = 0;// contactsNoDialogs.list.rowAtY(mouseY - otherStart, st::dlgHeight); - contactSel = true; - } + auto newSel = dialogs->rowAtY(mouseY, st::dlgHeight); + int otherStart = dialogs->size() * st::dlgHeight; if (newSel != sel) { updateSelectedRow(); sel = newSel; @@ -419,7 +396,7 @@ void DialogsInner::resizeEvent(QResizeEvent *e) { _cancelSearchInPeer.move(width() - st::dlgPaddingHor - st::btnCancelSearch.width, (st::dlgHeight - st::btnCancelSearch.height) / 2); } -void DialogsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) { +void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { if (_state == FilteredState || _state == SearchedState) { for (FilteredDialogs::iterator i = _filterResults.begin(); i != _filterResults.end();) { if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts! @@ -442,10 +419,10 @@ void DialogsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) { void DialogsInner::createDialog(History *history) { bool creating = !history->inChatList(); if (creating) { - DialogRow *mainRow = history->addToChatList(dialogs); - contactsNoDialogs.del(history->peer, mainRow); + Dialogs::Row *mainRow = history->addToChatList(dialogs.data()); + contactsNoDialogs->del(history->peer, mainRow); } - RefPair(int32, movedFrom, int32, movedTo) = history->adjustByPosInChatsList(dialogs); + RefPair(int32, movedFrom, int32, movedTo) = history->adjustByPosInChatsList(dialogs.data()); emit dialogMoved(movedFrom, movedTo); @@ -461,15 +438,15 @@ void DialogsInner::removeDialog(History *history) { if (history->peer == _menuPeer && _menu) { _menu->deleteLater(); } - if (sel && sel->history == history) { - sel = 0; + if (sel && sel->history() == history) { + sel = nullptr; } - history->removeFromChatList(dialogs); + history->removeFromChatList(dialogs.data()); history->clearNotifications(); if (App::wnd()) App::wnd()->notifyClear(history); - if (contacts.list.rowByPeer.constFind(history->peer->id) != contacts.list.rowByPeer.cend()) { - if (contactsNoDialogs.list.rowByPeer.constFind(history->peer->id) == contactsNoDialogs.list.rowByPeer.cend()) { - contactsNoDialogs.addByName(history); + if (contacts->contains(history->peer->id)) { + if (!contactsNoDialogs->contains(history->peer->id)) { + contactsNoDialogs->addByName(history); } } @@ -480,12 +457,12 @@ void DialogsInner::removeDialog(History *history) { refresh(); } -void DialogsInner::dlgUpdated(DialogRow *row) { +void DialogsInner::dlgUpdated(Dialogs::Row *row) { if (_state == DefaultState) { - update(0, row->pos * st::dlgHeight, fullWidth(), st::dlgHeight); + update(0, row->pos() * st::dlgHeight, fullWidth(), st::dlgHeight); } else if (_state == FilteredState || _state == SearchedState) { for (int32 i = 0, l = _filterResults.size(); i < l; ++i) { - if (_filterResults.at(i)->history == row->history) { + if (_filterResults.at(i)->history() == row->history()) { update(0, i * st::dlgHeight, fullWidth(), st::dlgHeight); break; } @@ -495,15 +472,13 @@ void DialogsInner::dlgUpdated(DialogRow *row) { void DialogsInner::dlgUpdated(History *history, MsgId msgId) { if (_state == DefaultState) { - DialogRow *row = 0; - DialogsList::RowByPeer::iterator i = dialogs.list.rowByPeer.find(history->peer->id); - if (i != dialogs.list.rowByPeer.cend()) { - update(0, i.value()->pos * st::dlgHeight, fullWidth(), st::dlgHeight); + if (auto row = dialogs->getRow(history->peer->id)) { + update(0, row->pos() * st::dlgHeight, fullWidth(), st::dlgHeight); } } else if (_state == FilteredState || _state == SearchedState) { int32 cnt = 0, add = filteredOffset(); for (FilteredDialogs::const_iterator i = _filterResults.cbegin(), e = _filterResults.cend(); i != e; ++i) { - if ((*i)->history == history) { + if ((*i)->history() == history) { update(0, add + cnt * st::dlgHeight, fullWidth(), st::dlgHeight); break; } @@ -522,7 +497,7 @@ void DialogsInner::dlgUpdated(History *history, MsgId msgId) { if (!_searchResults.isEmpty()) { int32 cnt = 0, add = searchedOffset(); for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) { - if ((*i)->_item->history() == history && (*i)->_item->id == msgId) { + if ((*i)->item()->history() == history && (*i)->item()->id == msgId) { update(0, add + cnt * st::dlgHeight, fullWidth(), st::dlgHeight); break; } @@ -547,12 +522,12 @@ void DialogsInner::updateSelectedRow(PeerData *peer) { } } } else if (sel) { - update(0, sel->pos * st::dlgHeight, fullWidth(), st::dlgHeight); + update(0, sel->pos() * st::dlgHeight, fullWidth(), st::dlgHeight); } } else if (_state == FilteredState || _state == SearchedState) { if (peer) { for (int32 i = 0, l = _filterResults.size(); i != l; ++i) { - if (_filterResults.at(i)->history->peer == peer) { + if (_filterResults.at(i)->history()->peer == peer) { update(0, filteredOffset() + i * st::dlgHeight, fullWidth(), st::dlgHeight); break; } @@ -600,10 +575,10 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) { History *history = 0; if (_state == DefaultState) { - if (sel) history = sel->history; + if (sel) history = sel->history(); } else if (_state == FilteredState || _state == SearchedState) { if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - history = _filterResults[_filteredSel]->history; + history = _filterResults[_filteredSel]->history(); } } if (!history) return; @@ -732,9 +707,9 @@ void DialogsInner::onParentGeometryChanged() { } void DialogsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { - dialogs.peerNameChanged(peer, oldNames, oldChars); - contactsNoDialogs.peerNameChanged(peer, oldNames, oldChars); - contacts.peerNameChanged(peer, oldNames, oldChars); + dialogs->peerNameChanged(peer, oldNames, oldChars); + contactsNoDialogs->peerNameChanged(peer, oldNames, oldChars); + contacts->peerNameChanged(peer, oldNames, oldChars); update(); } @@ -775,35 +750,36 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { _state = FilteredState; _filterResults.clear(); if (!_searchInPeer && !f.isEmpty()) { - DialogsList *dialogsToFilter = 0, *contactsNoDialogsToFilter = 0; - if (dialogs.list.count) { + const Dialogs::List *toFilter = nullptr; + if (!dialogs->isEmpty()) { for (fi = fb; fi != fe; ++fi) { - DialogsIndexed::DialogsIndex::iterator i = dialogs.index.find(fi->at(0)); - if (i == dialogs.index.cend()) { - dialogsToFilter = 0; + auto found = dialogs->filtered(fi->at(0)); + if (found->isEmpty()) { + toFilter = nullptr; break; } - if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) { - dialogsToFilter = i.value(); + if (!toFilter || toFilter->size() > found->size()) { + toFilter = found; } } } - if (contactsNoDialogs.list.count) { + const Dialogs::List *toFilterContacts = nullptr; + if (!contactsNoDialogs->isEmpty()) { for (fi = fb; fi != fe; ++fi) { - DialogsIndexed::DialogsIndex::iterator i = contactsNoDialogs.index.find(fi->at(0)); - if (i == contactsNoDialogs.index.cend()) { - contactsNoDialogsToFilter = 0; + auto found = contactsNoDialogs->filtered(fi->at(0)); + if (found->isEmpty()) { + toFilterContacts = nullptr; break; } - if (!contactsNoDialogsToFilter || contactsNoDialogsToFilter->count > i.value()->count) { - contactsNoDialogsToFilter = i.value(); + if (!toFilterContacts || toFilterContacts->size() > found->size()) { + toFilterContacts = found; } } } - _filterResults.reserve((dialogsToFilter ? dialogsToFilter->count : 0) + (contactsNoDialogsToFilter ? contactsNoDialogsToFilter->count : 0)); - if (dialogsToFilter && dialogsToFilter->count) { - for (DialogRow *i = dialogsToFilter->begin, *e = dialogsToFilter->end; i != e; i = i->next) { - const PeerData::Names &names(i->history->peer->names); + _filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0)); + if (toFilter) { + for_const (auto row, *toFilter) { + const PeerData::Names &names(row->history()->peer->names); PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; for (fi = fb; fi != fe; ++fi) { QString filterName(*fi); @@ -817,13 +793,13 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { } } if (fi == fe) { - _filterResults.push_back(i); + _filterResults.push_back(row); } } } - if (contactsNoDialogsToFilter && contactsNoDialogsToFilter->count) { - for (DialogRow *i = contactsNoDialogsToFilter->begin, *e = contactsNoDialogsToFilter->end; i != e; i = i->next) { - const PeerData::Names &names(i->history->peer->names); + if (toFilterContacts) { + for_const (auto row, *toFilterContacts) { + const PeerData::Names &names(row->history()->peer->names); PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; for (fi = fb; fi != fe; ++fi) { QString filterName(*fi); @@ -837,7 +813,7 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) { } } if (fi == fe) { - _filterResults.push_back(i); + _filterResults.push_back(row); } } } @@ -916,14 +892,14 @@ PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) { selByMouse = true; onUpdateSelected(true); if (_state == DefaultState) { - if (sel) return sel->history->peer; + if (sel) return sel->history()->peer; } else if (_state == FilteredState || _state == SearchedState) { if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - return _filterResults[_filteredSel]->history->peer; + return _filterResults[_filteredSel]->history()->peer; } else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) { return _peopleResults[_peopleSel]; } else if (_searchedSel >= 0 && _searchedSel < _searchResults.size()) { - return _searchResults[_searchedSel]->_item->history()->peer; + return _searchResults[_searchedSel]->item()->history()->peer; } } return 0; @@ -932,7 +908,7 @@ PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) { void DialogsInner::itemRemoved(HistoryItem *item) { int wasCount = _searchResults.size(); for (int i = 0; i < _searchResults.size();) { - if (_searchResults[i]->_item == item) { + if (_searchResults[i]->item() == item) { _searchResults.remove(i); if (item->history()->peer == _searchInMigrated) { if (_searchedMigratedCount > 0) --_searchedMigratedCount; @@ -990,7 +966,7 @@ void DialogsInner::dialogsReceived(const QVector &added) { if (!history->lastMsgDate.isNull()) { addSavedPeersAfter(history->lastMsgDate); } - contactsNoDialogs.del(history->peer); + contactsNoDialogs->del(history->peer); if (history->peer->migrateFrom()) { removeDialog(App::historyLoaded(history->peer->migrateFrom()->id)); } else if (history->peer->migrateTo() && history->peer->migrateTo()->amIn()) { @@ -1000,9 +976,8 @@ void DialogsInner::dialogsReceived(const QVector &added) { } if (App::wnd()) App::wnd()->updateCounter(); - if (!sel && dialogs.list.count) { - sel = dialogs.list.begin; - contactSel = false; + if (!sel && !dialogs->isEmpty()) { + sel = *dialogs->cbegin(); } refresh(); } @@ -1012,7 +987,7 @@ void DialogsInner::addSavedPeersAfter(const QDateTime &date) { while (!saved.isEmpty() && (date.isNull() || date < saved.lastKey())) { History *history = App::history(saved.last()->id); history->setChatsListDate(saved.lastKey()); - contactsNoDialogs.del(history->peer); + contactsNoDialogs->del(history->peer); saved.remove(saved.lastKey(), saved.last()); } } @@ -1030,7 +1005,7 @@ bool DialogsInner::searchReceived(const QVector &messages, DialogsSe HistoryItem *item = App::histories().addNewMessage(*i, NewMessageExisting); int32 lastDate = dateFromMessage(*i); if (lastDate) { - _searchResults.push_back(new FakeDialogRow(item)); + _searchResults.push_back(new Dialogs::FakeRow(item)); lastDateFound = lastDate; if (type == DialogsSearchFromStart || type == DialogsSearchFromOffset) { _lastSearchDate = lastDateFound; @@ -1087,30 +1062,26 @@ void DialogsInner::contactsReceived(const QVector &contacts) { } } } - if (!sel && contactsNoDialogs.list.count && false) { - sel = contactsNoDialogs.list.begin; - contactSel = true; - } refresh(); } void DialogsInner::notify_userIsContactChanged(UserData *user, bool fromThisApp) { if (user->contact > 0) { History *history = App::history(user->id); - contacts.addByName(history); - DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(user->id); - if (i == dialogs.list.rowByPeer.cend()) { - contactsNoDialogs.addByName(history); - } else if (fromThisApp) { - sel = i.value(); - contactSel = false; + contacts->addByName(history); + if (auto row = dialogs->getRow(user->id)) { + if (fromThisApp) { + sel = row; + } + } else { + contactsNoDialogs->addByName(history); } } else { - if (sel && sel->history->peer == user) { - sel = 0; + if (sel && sel->history()->peer == user) { + sel = nullptr; } - contactsNoDialogs.del(user); - contacts.del(user); + contactsNoDialogs->del(user); + contacts->del(user); } refresh(); } @@ -1118,7 +1089,7 @@ void DialogsInner::notify_userIsContactChanged(UserData *user, bool fromThisApp) void DialogsInner::refresh(bool toTop) { int32 h = 0; if (_state == DefaultState) { - h = (dialogs.list.count/* + contactsNoDialogs.list.count*/) * st::dlgHeight; + h = (dialogs->size()/* + contactsNoDialogs->size()*/) * st::dlgHeight; if (h) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); } else { @@ -1149,8 +1120,7 @@ void DialogsInner::setMouseSel(bool msel, bool toTop) { selByMouse = msel; if (!selByMouse && toTop) { if (_state == DefaultState) { - sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0)); - contactSel = !dialogs.list.count && contactsNoDialogs.list.count; + sel = !dialogs->isEmpty() ? *dialogs->cbegin() : nullptr; } else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search _filteredSel = _peopleSel = _searchedSel = _hashtagSel = -1; setCursor(style::cur_default); @@ -1213,29 +1183,23 @@ void DialogsInner::clearFilter() { void DialogsInner::selectSkip(int32 direction) { if (_state == DefaultState) { if (!sel) { - if (dialogs.list.count && direction > 0) { - sel = dialogs.list.begin; - } else if (false && contactsNoDialogs.list.count && direction > 0) { - sel = contactsNoDialogs.list.begin; + if (!dialogs->isEmpty() && direction > 0) { + sel = *dialogs->cbegin(); } else { return; } } else if (direction > 0) { - if (sel->next->next) { - sel = sel->next; - } else if (false && sel->next == dialogs.list.end && contactsNoDialogs.list.count) { - sel = contactsNoDialogs.list.begin; - contactSel = true; + auto next = dialogs->cfind(sel); + if (++next != dialogs->cend()) { + sel = *next; } } else { - if (sel->prev) { - sel = sel->prev; - } else if (false && sel == contactsNoDialogs.list.begin && dialogs.list.count) { - sel = dialogs.list.end->prev; - contactSel = false; + auto prev = dialogs->cfind(sel); + if (prev != dialogs->cbegin()) { + sel = *(--prev); } } - int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight; + int32 fromY = sel->pos() * st::dlgHeight; emit mustScrollTo(fromY, fromY + st::dlgHeight); } else if (_state == FilteredState || _state == SearchedState) { if (_hashtagResults.isEmpty() && _filterResults.isEmpty() && _peopleResults.isEmpty() && _searchResults.isEmpty()) return; @@ -1285,19 +1249,13 @@ void DialogsInner::selectSkip(int32 direction) { void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) { int32 fromY = -1; if (_state == DefaultState) { - DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(peer); - if (i != dialogs.list.rowByPeer.cend()) { - fromY = i.value()->pos * st::dlgHeight; - } else if (false) { - i = contactsNoDialogs.list.rowByPeer.constFind(peer); - if (i != contactsNoDialogs.list.rowByPeer.cend()) { - fromY = (i.value()->pos + dialogs.list.count) * st::dlgHeight; - } + if (auto row = dialogs->getRow(peer)) { + fromY = row->pos() * st::dlgHeight; } } else if (_state == FilteredState || _state == SearchedState) { if (msgId) { for (int32 i = 0, c = _searchResults.size(); i < c; ++i) { - if (_searchResults[i]->_item->history()->peer->id == peer && _searchResults[i]->_item->id == msgId) { + if (_searchResults[i]->item()->history()->peer->id == peer && _searchResults[i]->item()->id == msgId) { fromY = searchedOffset() + i * st::dlgHeight; break; } @@ -1305,7 +1263,7 @@ void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) { } if (fromY < 0) { for (int32 i = 0, c = _filterResults.size(); i < c; ++i) { - if (_filterResults[i]->history->peer->id == peer) { + if (_filterResults[i]->history()->peer->id == peer) { fromY = filteredOffset() + (i * st::dlgHeight); break; } @@ -1318,41 +1276,25 @@ void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) { } void DialogsInner::selectSkipPage(int32 pixels, int32 direction) { - int32 toSkip = pixels / int32(st::dlgHeight); + int toSkip = pixels / int(st::dlgHeight); if (_state == DefaultState) { if (!sel) { - if (direction > 0 && dialogs.list.count) { - sel = dialogs.list.begin; - } else if (false && direction > 0 && contactsNoDialogs.list.count) { - sel = contactsNoDialogs.list.begin; + if (direction > 0 && !dialogs->isEmpty()) { + sel = *dialogs->cbegin(); } else { return; } } if (direction > 0) { - while (toSkip-- && sel->next->next) { - sel = sel->next; - } - if (false && toSkip >= 0 && sel->next == dialogs.list.end && contactsNoDialogs.list.count) { - sel = contactsNoDialogs.list.begin; - while (toSkip-- && sel->next->next) { - sel = sel->next; - } - contactSel = true; + for (auto i = dialogs->cfind(sel), end = dialogs->cend(); i != end && (toSkip--); ++i) { + sel = *i; } } else { - while (toSkip-- && sel->prev) { - sel = sel->prev; - } - if (toSkip >= 0 && sel == contactsNoDialogs.list.begin && dialogs.list.count) { - sel = dialogs.list.end->prev; - while (toSkip-- && sel->prev) { - sel = sel->prev; - } - contactSel = false; + for (auto i = dialogs->cfind(sel), b = dialogs->cbegin(); i != b && (toSkip--); --i) { + sel = *i; } } - int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight; + int fromY = sel->pos() * st::dlgHeight; emit mustScrollTo(fromY, fromY + st::dlgHeight); } else { return selectSkip(direction * toSkip); @@ -1366,23 +1308,19 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) { int32 yTo = yFrom + parentWidget()->height() * 5; MTP::clearLoaderPriorities(); if (_state == DefaultState) { - int32 otherStart = dialogs.list.count * st::dlgHeight; + int32 otherStart = dialogs->size() * st::dlgHeight; if (yFrom < otherStart) { - dialogs.list.adjustCurrent(yFrom, st::dlgHeight); - for (DialogRow *row = dialogs.list.current; row != dialogs.list.end && (row->pos * st::dlgHeight) < yTo; row = row->next) { - row->history->peer->loadUserpic(); + for (auto i = dialogs->cfind(yFrom, st::dlgHeight), end = dialogs->cend(); i != end; ++i) { + if (((*i)->pos() * st::dlgHeight) >= yTo) { + break; + } + (*i)->history()->peer->loadUserpic(); } yFrom = 0; } else { yFrom -= otherStart; } yTo -= otherStart; - if (yTo > 0) { - contactsNoDialogs.list.adjustCurrent(yFrom, st::dlgHeight); - for (DialogRow *row = contactsNoDialogs.list.current; row != contactsNoDialogs.list.end && (row->pos * st::dlgHeight) < yTo; row = row->next) { - row->history->peer->loadUserpic(); - } - } } else if (_state == FilteredState || _state == SearchedState) { int32 from = (yFrom - filteredOffset()) / st::dlgHeight; if (from < 0) from = 0; @@ -1391,7 +1329,7 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) { if (to > _filterResults.size()) to = _filterResults.size(); for (; from < to; ++from) { - _filterResults[from]->history->peer->loadUserpic(); + _filterResults[from]->history()->peer->loadUserpic(); } } @@ -1412,7 +1350,7 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) { if (to > _searchResults.size()) to = _searchResults.size(); for (; from < to; ++from) { - _searchResults[from]->_item->history()->peer->loadUserpic(); + _searchResults[from]->item()->history()->peer->loadUserpic(); } } } @@ -1422,7 +1360,7 @@ bool DialogsInner::choosePeer() { History *history = 0; MsgId msgId = ShowAtUnreadMsgId; if (_state == DefaultState) { - if (sel) history = sel->history; + if (sel) history = sel->history(); } else if (_state == FilteredState || _state == SearchedState) { if (_hashtagSel >= 0 && _hashtagSel < _hashtagResults.size()) { QString hashtag = _hashtagResults.at(_hashtagSel); @@ -1450,12 +1388,12 @@ bool DialogsInner::choosePeer() { return true; } if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) { - history = _filterResults[_filteredSel]->history; + history = _filterResults[_filteredSel]->history(); } else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) { history = App::history(_peopleResults[_peopleSel]->id); } else if (_searchedSel >= 0 && _searchedSel < _searchResults.size()) { - history = _searchResults[_searchedSel]->_item->history(); - msgId = _searchResults[_searchedSel]->_item->id; + history = _searchResults[_searchedSel]->item()->history(); + msgId = _searchResults[_searchedSel]->item()->id; } } if (history) { @@ -1504,8 +1442,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) { } void DialogsInner::destroyData() { - sel = 0; - contactSel = false; + sel = nullptr; _hashtagSel = -1; _hashtagResults.clear(); _filteredSel = -1; @@ -1520,54 +1457,39 @@ void DialogsInner::destroyData() { void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const { if (!inPeer) { - outPeer = 0; + outPeer = nullptr; outMsg = 0; return; } if (_state == DefaultState) { - DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(inPeer->id); - if (i == dialogs.list.rowByPeer.constEnd()) { - i = contactsNoDialogs.list.rowByPeer.constFind(inPeer->id); - if (i == contactsNoDialogs.list.rowByPeer.cend()) { - outPeer = 0; - outMsg = 0; - return; - } - if (i.value()->prev) { - outPeer = i.value()->prev->history->peer; - outMsg = ShowAtUnreadMsgId; - return; - } else if (dialogs.list.count) { - outPeer = dialogs.list.end->prev->history->peer; + if (auto row = dialogs->getRow(inPeer->id)) { + auto i = dialogs->cfind(row); + if (i != dialogs->cbegin()) { + outPeer = (*(--i))->history()->peer; outMsg = ShowAtUnreadMsgId; return; } - outPeer = 0; - outMsg = 0; - return; - } - if (i.value()->prev) { - outPeer = i.value()->prev->history->peer; - outMsg = ShowAtUnreadMsgId; - return; } + outPeer = nullptr; + outMsg = 0; + return; } else if (_state == FilteredState || _state == SearchedState) { if (inMsg && !_searchResults.isEmpty()) { for (SearchResults::const_iterator b = _searchResults.cbegin(), i = b + 1, e = _searchResults.cend(); i != e; ++i) { - if ((*i)->_item->history()->peer == inPeer && (*i)->_item->id == inMsg) { + if ((*i)->item()->history()->peer == inPeer && (*i)->item()->id == inMsg) { SearchResults::const_iterator j = i - 1; - outPeer = (*j)->_item->history()->peer; - outMsg = (*j)->_item->id; + outPeer = (*j)->item()->history()->peer; + outMsg = (*j)->item()->id; return; } } - if (_searchResults.at(0)->_item->history()->peer == inPeer && _searchResults.at(0)->_item->id == inMsg) { + if (_searchResults.at(0)->item()->history()->peer == inPeer && _searchResults.at(0)->item()->id == inMsg) { outMsg = ShowAtUnreadMsgId; if (_peopleResults.isEmpty()) { if (_filterResults.isEmpty()) { - outPeer = 0; + outPeer = nullptr; } else { - outPeer = _filterResults.back()->history->peer; + outPeer = _filterResults.back()->history()->peer; } } else { outPeer = _peopleResults.back(); @@ -1576,7 +1498,7 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou } } if (!_peopleResults.isEmpty() && _peopleResults.at(0) == inPeer) { - outPeer = _filterResults.isEmpty() ? 0 : _filterResults.back()->history->peer; + outPeer = _filterResults.isEmpty() ? 0 : _filterResults.back()->history()->peer; outMsg = ShowAtUnreadMsgId; return; } @@ -1589,65 +1511,49 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou } } } - if (_filterResults.isEmpty() || _filterResults.at(0)->history->peer == inPeer) { - outPeer = 0; + if (_filterResults.isEmpty() || _filterResults.at(0)->history()->peer == inPeer) { + outPeer = nullptr; outMsg = 0; return; } for (FilteredDialogs::const_iterator b = _filterResults.cbegin(), i = b + 1, e = _filterResults.cend(); i != e; ++i) { - if ((*i)->history->peer == inPeer) { - outPeer = (*(i - 1))->history->peer; + if ((*i)->history()->peer == inPeer) { + outPeer = (*(i - 1))->history()->peer; outMsg = ShowAtUnreadMsgId; return; } } } - outPeer = 0; + outPeer = nullptr; outMsg = 0; } void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const { if (!inPeer) { - outPeer = 0; + outPeer = nullptr; outMsg = 0; return; } if (_state == DefaultState) { - DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(inPeer->id); - if (i == dialogs.list.rowByPeer.constEnd()) { - //i = contactsNoDialogs.list.rowByPeer.constFind(inPeer->id); - //if (i == contactsNoDialogs.list.rowByPeer.cend()) { - // outPeer = 0; - // outMsg = 0; - // return; - //} - //if (i.value()->next != contactsNoDialogs.list.end) { - // outPeer = i.value()->next->history->peer; - // outMsg = ShowAtUnreadMsgId; - // return; - //} - outPeer = 0; - outMsg = 0; - return; - } - - if (i.value()->next != dialogs.list.end) { - outPeer = i.value()->next->history->peer; - outMsg = ShowAtUnreadMsgId; - return; - } else if (false && contactsNoDialogs.list.count) { - outPeer = contactsNoDialogs.list.begin->history->peer; - outMsg = ShowAtUnreadMsgId; - return; + if (auto row = dialogs->getRow(inPeer->id)) { + auto i = dialogs->cfind(row) + 1; + if (i != dialogs->cend()) { + outPeer = (*i)->history()->peer; + outMsg = ShowAtUnreadMsgId; + return; + } } + outPeer = nullptr; + outMsg = 0; + return; } else if (_state == FilteredState || _state == SearchedState) { if (inMsg) { for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) { - if ((*i)->_item->history()->peer == inPeer && (*i)->_item->id == inMsg) { + if ((*i)->item()->history()->peer == inPeer && (*i)->item()->id == inMsg) { ++i; - outPeer = (i == e) ? 0 : (*i)->_item->history()->peer; - outMsg = (i == e) ? 0 : (*i)->_item->id; + outPeer = (i == e) ? nullptr : (*i)->item()->history()->peer; + outMsg = (i == e) ? 0 : (*i)->item()->id; return; } } @@ -1656,42 +1562,42 @@ void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&out if ((*i) == inPeer) { ++i; if (i == e && !_searchResults.isEmpty()) { - outPeer = _searchResults.front()->_item->history()->peer; - outMsg = _searchResults.front()->_item->id; + outPeer = _searchResults.front()->item()->history()->peer; + outMsg = _searchResults.front()->item()->id; } else { - outPeer = (i == e) ? 0 : (*i); + outPeer = (i == e) ? nullptr : (*i); outMsg = ShowAtUnreadMsgId; } return; } } for (FilteredDialogs::const_iterator i = _filterResults.cbegin(), e = _filterResults.cend(); i != e; ++i) { - if ((*i)->history->peer == inPeer) { + if ((*i)->history()->peer == inPeer) { ++i; if (i == e && !_peopleResults.isEmpty()) { outPeer = _peopleResults.front(); outMsg = ShowAtUnreadMsgId; } else if (i == e && !_searchResults.isEmpty()) { - outPeer = _searchResults.front()->_item->history()->peer; - outMsg = _searchResults.front()->_item->id; + outPeer = _searchResults.front()->item()->history()->peer; + outMsg = _searchResults.front()->item()->id; } else { - outPeer = (i == e) ? 0 : (*i)->history->peer; + outPeer = (i == e) ? nullptr : (*i)->history()->peer; outMsg = ShowAtUnreadMsgId; } return; } } } - outPeer = 0; + outPeer = nullptr; outMsg = 0; } -DialogsIndexed &DialogsInner::contactsList() { - return contacts; +Dialogs::IndexedList *DialogsInner::contactsList() { + return contacts.data(); } -DialogsIndexed &DialogsInner::dialogsList() { - return dialogs; +Dialogs::IndexedList *DialogsInner::dialogsList() { + return dialogs.data(); } DialogsInner::FilteredDialogs &DialogsInner::filteredList() { @@ -1802,7 +1708,7 @@ void DialogsWidget::createDialog(History *history) { } } -void DialogsWidget::dlgUpdated(DialogRow *row) { +void DialogsWidget::dlgUpdated(Dialogs::Row *row) { _inner.dlgUpdated(row); } @@ -2365,7 +2271,7 @@ void DialogsWidget::onListScroll() { if (_scroll.scrollTop() > (_inner.searchList().size() + _inner.filteredList().size() + _inner.peopleList().size()) * st::dlgHeight - PreloadHeightsCount * _scroll.height()) { onSearchMore(); } - } else if (_scroll.scrollTop() > _inner.dialogsList().list.count * st::dlgHeight - PreloadHeightsCount * _scroll.height()) { + } else if (_scroll.scrollTop() > _inner.dialogsList()->size() * st::dlgHeight - PreloadHeightsCount * _scroll.height()) { loadDialogs(); } } @@ -2541,11 +2447,11 @@ void DialogsWidget::removeDialog(History *history) { onFilterUpdate(); } -DialogsIndexed &DialogsWidget::contactsList() { +Dialogs::IndexedList *DialogsWidget::contactsList() { return _inner.contactsList(); } -DialogsIndexed &DialogsWidget::dialogsList() { +Dialogs::IndexedList *DialogsWidget::dialogsList() { return _inner.dialogsList(); } diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 75ef10e49..3460e8b69 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -21,6 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once class MainWidget; +namespace Dialogs { +class Row; +class FakeRow; +class IndexedList; +} // namespace Dialogs enum DialogsSearchRequestType { DialogsSearchFromStart, @@ -67,7 +72,7 @@ public: void selectSkipPage(int32 pixels, int32 direction); void createDialog(History *history); - void dlgUpdated(DialogRow *row); + void dlgUpdated(Dialogs::Row *row); void dlgUpdated(History *row, MsgId msgId); void removeDialog(History *history); @@ -84,12 +89,12 @@ public: void peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const; void scrollToPeer(const PeerId &peer, MsgId msgId); - typedef QVector FilteredDialogs; + typedef QVector FilteredDialogs; typedef QVector PeopleResults; - typedef QVector SearchResults; + typedef QVector SearchResults; - DialogsIndexed &contactsList(); - DialogsIndexed &dialogsList(); + Dialogs::IndexedList *contactsList(); + Dialogs::IndexedList *dialogsList(); FilteredDialogs &filteredList(); PeopleResults &peopleList(); SearchResults &searchList(); @@ -129,7 +134,7 @@ public slots: void onParentGeometryChanged(); void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); void onPeerPhotoChanged(PeerData *peer); - void onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow); + void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); void onContextProfile(); void onContextToggleNotifications(); @@ -165,46 +170,52 @@ private: bool menuPeerMuted(); void contextBlockDone(QPair data, const MTPBool &result); - DialogsIndexed dialogs; - DialogsIndexed contactsNoDialogs; - DialogsIndexed contacts; - DialogRow *sel; - bool contactSel; - bool selByMouse; + using DialogsList = UniquePointer; + DialogsList dialogs; + DialogsList contactsNoDialogs; + DialogsList contacts; + Dialogs::Row *sel = nullptr; + bool selByMouse = false; QString _filter, _hashtagFilter; QStringList _hashtagResults; - int32 _hashtagSel; + int _hashtagSel = -1; FilteredDialogs _filterResults; - int32 _filteredSel; + int _filteredSel = -1; SearchResults _searchResults; - int32 _searchedCount, _searchedMigratedCount, _searchedSel; + int _searchedCount = 0; + int _searchedMigratedCount = 0; + int _searchedSel = -1; QString _peopleQuery; PeopleResults _peopleResults; - int32 _peopleSel; + int _peopleSel = -1; - int32 _lastSearchDate; - PeerData *_lastSearchPeer; - MsgId _lastSearchId, _lastSearchMigratedId; + int _lastSearchDate = 0; + PeerData *_lastSearchPeer = nullptr; + MsgId _lastSearchId = 0; + MsgId _lastSearchMigratedId = 0; - State _state; + State _state = DefaultState; QPoint lastMousePos; - void paintDialog(QPainter &p, DialogRow *dialog); + void paintDialog(QPainter &p, Dialogs::Row *dialog); LinkButton _addContactLnk; IconedButton _cancelSearchInPeer; - bool _overDelete; + bool _overDelete = false; - PeerData *_searchInPeer, *_searchInMigrated, *_menuPeer, *_menuActionPeer; + PeerData *_searchInPeer = nullptr; + PeerData *_searchInMigrated = nullptr; + PeerData *_menuPeer = nullptr; + PeerData *_menuActionPeer = nullptr; - PopupMenu *_menu; + PopupMenu *_menu = nullptr; }; @@ -233,7 +244,7 @@ public: void loadDialogs(); void createDialog(History *history); - void dlgUpdated(DialogRow *row); + void dlgUpdated(Dialogs::Row *row); void dlgUpdated(History *row, MsgId msgId); void dialogsToUp(); @@ -249,8 +260,8 @@ public: void removeDialog(History *history); - DialogsIndexed &contactsList(); - DialogsIndexed &dialogsList(); + Dialogs::IndexedList *contactsList(); + Dialogs::IndexedList *dialogsList(); void searchMessages(const QString &query, PeerData *inPeer = 0); void onSearchMore(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 94e8530d9..da6728515 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -19,18 +19,18 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "history.h" + +#include "dialogs/dialogs_indexed_list.h" #include "ui/style.h" #include "lang.h" - #include "mainwidget.h" #include "application.h" #include "fileuploader.h" #include "window.h" #include "ui/filedialog.h" - #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" - #include "audio.h" #include "localstorage.h" @@ -87,177 +87,6 @@ void historyInit() { _initTextOptions(); } -void DialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const { - QRect fullRect(0, 0, w, st::dlgHeight); - p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b); - if (onlyBackground) return; - - PeerData *userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); - userpicPeer->paintUserpicLeft(p, st::dlgPhotoSize, st::dlgPaddingHor, st::dlgPaddingVer, w); - - int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding; - int32 namewidth = w - nameleft - st::dlgPaddingHor; - QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height); - - // draw chat icon - if (history->peer->isChat() || history->peer->isMegagroup()) { - p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), App::sprite(), (act ? st::dlgActiveChatImg : st::dlgChatImg)); - rectForName.setLeft(rectForName.left() + st::dlgImgSkip); - } else if (history->peer->isChannel()) { - p.drawPixmap(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), App::sprite(), (act ? st::dlgActiveChannelImg : st::dlgChannelImg)); - rectForName.setLeft(rectForName.left() + st::dlgImgSkip); - } - - HistoryItem *last = history->lastMsg; - if (!last) { - p.setFont(st::dlgHistFont->f); - p.setPen((act ? st::dlgActiveColor : st::dlgSystemColor)->p); - if (history->typing.isEmpty() && history->sendActions.isEmpty()) { - p.drawText(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgFont->ascent + st::dlgSep, lang(lng_empty_history)); - } else { - history->typingText.drawElided(p, nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth); - } - } else { - // draw date - QDateTime now(QDateTime::currentDateTime()), lastTime(last->date); - QDate nowDate(now.date()), lastDate(lastTime.date()); - QString dt; - if (lastDate == nowDate) { - dt = lastTime.toString(cTimeFormat()); - } else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) { - dt = langDayOfWeek(lastDate); - } else { - dt = lastDate.toString(qsl("d.MM.yy")); - } - int32 dtWidth = st::dlgDateFont->width(dt); - rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip); - p.setFont(st::dlgDateFont->f); - p.setPen((act ? st::dlgActiveDateColor : st::dlgDateColor)->p); - p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt); - - // draw check - if (last->needCheck()) { - const style::sprite *check; - if (last->id > 0) { - if (last->unread()) { - check = act ? &st::dlgActiveCheckImg : &st::dlgCheckImg; - } else { - check = act ? &st::dlgActiveDblCheckImg: &st::dlgDblCheckImg; - } - } else { - check = act ? &st::dlgActiveSendImg : &st::dlgSendImg; - } - rectForName.setWidth(rectForName.width() - check->pxWidth() - st::dlgCheckSkip); - p.drawPixmap(QPoint(rectForName.left() + rectForName.width() + st::dlgCheckLeft, rectForName.top() + st::dlgCheckTop), App::sprite(), *check); - } - - // draw unread - int32 lastWidth = namewidth, unread = history->unreadCount; - if (history->peer->migrateFrom()) { - if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) { - unread += h->unreadCount; - } - } - if (unread) { - QString unreadStr = QString::number(unread); - int32 unreadWidth = st::dlgUnreadFont->width(unreadStr); - int32 unreadRectWidth = unreadWidth + 2 * st::dlgUnreadPaddingHor; - int32 unreadRectHeight = st::dlgUnreadFont->height + 2 * st::dlgUnreadPaddingVer; - int32 unreadRectLeft = w - st::dlgPaddingHor - unreadRectWidth; - int32 unreadRectTop = st::dlgHeight - st::dlgPaddingVer - unreadRectHeight; - lastWidth -= unreadRectWidth + st::dlgUnreadPaddingHor; - p.setBrush((act ? (history->mute ? st::dlgActiveUnreadMutedBG : st::dlgActiveUnreadBG) : (history->mute ? st::dlgUnreadMutedBG : st::dlgUnreadBG))->b); - p.setPen(Qt::NoPen); - p.drawRoundedRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight, st::dlgUnreadRadius, st::dlgUnreadRadius); - p.setFont(st::dlgUnreadFont->f); - p.setPen((act ? st::dlgActiveUnreadColor : st::dlgUnreadColor)->p); - p.drawText(unreadRectLeft + st::dlgUnreadPaddingHor, unreadRectTop + st::dlgUnreadPaddingVer + st::dlgUnreadFont->ascent, unreadStr); - } - if (history->typing.isEmpty() && history->sendActions.isEmpty()) { - last->drawInDialog(p, QRect(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth, st::dlgFont->height), act, history->textCachedFor, history->lastItemTextCache); - } else { - p.setPen((act ? st::dlgActiveColor : st::dlgSystemColor)->p); - history->typingText.drawElided(p, nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth); - } - } - - if (history->peer->isUser() && history->peer->isVerified()) { - rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x()); - p.drawSprite(rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (act ? st::verifiedCheckInv : st::verifiedCheck)); - } - - p.setPen((act ? st::dlgActiveColor : st::dlgNameColor)->p); - history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); -} - -void FakeDialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const { - QRect fullRect(0, 0, w, st::dlgHeight); - p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b); - if (onlyBackground) return; - - History *history = _item->history(); - PeerData *userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); - userpicPeer->paintUserpicLeft(p, st::dlgPhotoSize, st::dlgPaddingHor, st::dlgPaddingVer, w); - - int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding; - int32 namewidth = w - nameleft - st::dlgPaddingHor; - QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height); - - // draw chat icon - if (history->peer->isChat() || history->peer->isMegagroup()) { - p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), App::sprite(), (act ? st::dlgActiveChatImg : st::dlgChatImg)); - rectForName.setLeft(rectForName.left() + st::dlgImgSkip); - } else if (history->peer->isChannel()) { - p.drawPixmap(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), App::sprite(), (act ? st::dlgActiveChannelImg : st::dlgChannelImg)); - rectForName.setLeft(rectForName.left() + st::dlgImgSkip); - } - - // draw date - QDateTime now(QDateTime::currentDateTime()), lastTime(_item->date); - QDate nowDate(now.date()), lastDate(lastTime.date()); - QString dt; - if (lastDate == nowDate) { - dt = lastTime.toString(cTimeFormat()); - } else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) { - dt = langDayOfWeek(lastDate); - } else { - dt = lastDate.toString(qsl("d.MM.yy")); - } - int32 dtWidth = st::dlgDateFont->width(dt); - rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip); - p.setFont(st::dlgDateFont->f); - p.setPen((act ? st::dlgActiveDateColor : st::dlgDateColor)->p); - p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt); - - // draw check - if (_item->needCheck()) { - const style::sprite *check; - if (_item->id > 0) { - if (_item->unread()) { - check = act ? &st::dlgActiveCheckImg : &st::dlgCheckImg; - } else { - check = act ? &st::dlgActiveDblCheckImg : &st::dlgDblCheckImg; - } - } else { - check = act ? &st::dlgActiveSendImg : &st::dlgSendImg; - } - rectForName.setWidth(rectForName.width() - check->pxWidth() - st::dlgCheckSkip); - p.drawPixmap(QPoint(rectForName.left() + rectForName.width() + st::dlgCheckLeft, rectForName.top() + st::dlgCheckTop), App::sprite(), *check); - } - - // draw unread - int32 lastWidth = namewidth; - _item->drawInDialog(p, QRect(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, lastWidth, st::dlgFont->height), act, _cacheFor, _cache); - - if (history->peer->isUser() && history->peer->isVerified()) { - rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x()); - p.drawSprite(rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (act ? st::verifiedCheckInv : st::verifiedCheck)); - } - - p.setPen((act ? st::dlgActiveColor : st::dlgNameColor)->p); - history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); -} - History::History(const PeerId &peerId) : peer(App::peer(peerId)) , mute(isNotifyMuted(peer->notify)) { @@ -981,107 +810,6 @@ ChannelHistory::~ChannelHistory() { clearOnDestroy(); } -bool DialogsList::del(const PeerId &peerId, DialogRow *replacedBy) { - RowByPeer::iterator i = rowByPeer.find(peerId); - if (i == rowByPeer.cend()) return false; - - DialogRow *row = i.value(); - if (App::main()) { - emit App::main()->dialogRowReplaced(row, replacedBy); - } - - if (row == current) { - current = row->next; - } - for (DialogRow *change = row->next; change != end; change = change->next) { - change->pos--; - } - end->pos--; - remove(row); - delete row; - --count; - rowByPeer.erase(i); - - return true; -} - -void DialogsIndexed::peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { - if (sortMode == DialogsSortByName) { - DialogRow *mainRow = list.adjustByName(peer); - if (!mainRow) return; - - History *history = mainRow->history; - - PeerData::NameFirstChars toRemove = oldChars, toAdd; - for (PeerData::NameFirstChars::const_iterator i = peer->chars.cbegin(), e = peer->chars.cend(); i != e; ++i) { - PeerData::NameFirstChars::iterator j = toRemove.find(*i); - if (j == toRemove.cend()) { - toAdd.insert(*i); - } else { - toRemove.erase(j); - DialogsIndex::iterator k = index.find(*i); - if (k != index.cend()) { - k.value()->adjustByName(peer); - } - } - } - for (PeerData::NameFirstChars::const_iterator i = toRemove.cbegin(), e = toRemove.cend(); i != e; ++i) { - DialogsIndex::iterator j = index.find(*i); - if (j != index.cend()) { - j.value()->del(peer->id, mainRow); - } - } - if (!toAdd.isEmpty()) { - for (PeerData::NameFirstChars::const_iterator i = toAdd.cbegin(), e = toAdd.cend(); i != e; ++i) { - DialogsIndex::iterator j = index.find(*i); - if (j == index.cend()) { - j = index.insert(*i, new DialogsList(sortMode)); - } - j.value()->addByName(history); - } - } - } else { - DialogsList::RowByPeer::const_iterator i = list.rowByPeer.find(peer->id); - if (i == list.rowByPeer.cend()) return; - - DialogRow *mainRow = i.value(); - History *history = mainRow->history; - - PeerData::NameFirstChars toRemove = oldChars, toAdd; - for (PeerData::NameFirstChars::const_iterator i = peer->chars.cbegin(), e = peer->chars.cend(); i != e; ++i) { - PeerData::NameFirstChars::iterator j = toRemove.find(*i); - if (j == toRemove.cend()) { - toAdd.insert(*i); - } else { - toRemove.erase(j); - } - } - for (PeerData::NameFirstChars::const_iterator i = toRemove.cbegin(), e = toRemove.cend(); i != e; ++i) { - if (sortMode == DialogsSortByDate) history->removeChatListEntryByLetter(*i); - DialogsIndex::iterator j = index.find(*i); - if (j != index.cend()) { - j.value()->del(peer->id, mainRow); - } - } - for (PeerData::NameFirstChars::const_iterator i = toAdd.cbegin(), e = toAdd.cend(); i != e; ++i) { - DialogsIndex::iterator j = index.find(*i); - if (j == index.cend()) { - j = index.insert(*i, new DialogsList(sortMode)); - } - DialogRow *row = j.value()->addToEnd(history); - if (sortMode == DialogsSortByDate) history->addChatListEntryByLetter(*i, row); - } - } -} - -void DialogsIndexed::clear() { - for (DialogsIndex::iterator i = index.begin(), e = index.end(); i != e; ++i) { - delete i.value(); - } - index.clear(); - list.clear(); -} - History *Histories::find(const PeerId &peerId) { Map::const_iterator i = map.constFind(peerId); return (i == map.cend()) ? 0 : i.value(); @@ -2469,17 +2197,23 @@ void History::clearOnDestroy() { clearBlocks(false); } -QPair History::adjustByPosInChatsList(DialogsIndexed &indexed) { - DialogRow *lnk = mainChatListLink(); - int32 movedFrom = lnk->pos * st::dlgHeight; - indexed.adjustByPos(_chatListLinks); - int32 movedTo = lnk->pos * st::dlgHeight; +QPair History::adjustByPosInChatsList(Dialogs::IndexedList *indexed) { + t_assert(indexed != nullptr); + Dialogs::Row *lnk = mainChatListLink(); + int32 movedFrom = lnk->pos() * st::dlgHeight; + indexed->adjustByPos(_chatListLinks); + int32 movedTo = lnk->pos() * st::dlgHeight; return qMakePair(movedFrom, movedTo); } -DialogRow *History::addToChatList(DialogsIndexed &indexed) { +int History::posInChatList() const { + return mainChatListLink()->pos(); +} + +Dialogs::Row *History::addToChatList(Dialogs::IndexedList *indexed) { + t_assert(indexed != nullptr); if (!inChatList()) { - _chatListLinks = indexed.addToEnd(this); + _chatListLinks = indexed->addToEnd(this); if (unreadCount) { App::histories().unreadIncrement(unreadCount, mute); if (App::wnd()) App::wnd()->updateCounter(); @@ -2488,9 +2222,10 @@ DialogRow *History::addToChatList(DialogsIndexed &indexed) { return mainChatListLink(); } -void History::removeFromChatList(DialogsIndexed &indexed) { +void History::removeFromChatList(Dialogs::IndexedList *indexed) { + t_assert(indexed != nullptr); if (inChatList()) { - indexed.del(peer); + indexed->del(peer); _chatListLinks.clear(); if (unreadCount) { App::histories().unreadIncrement(-unreadCount, mute); @@ -2506,7 +2241,7 @@ void History::removeChatListEntryByLetter(QChar letter) { } } -void History::addChatListEntryByLetter(QChar letter, DialogRow *row) { +void History::addChatListEntryByLetter(QChar letter, Dialogs::Row *row) { t_assert(letter != 0); if (inChatList()) { _chatListLinks.insert(letter, row); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 0cd6ee613..b65a34eed 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -27,6 +27,7 @@ class HistoryItem; typedef QMap SelectedItemSet; #include "structs.h" +#include "dialogs/dialogs_common.h" enum NewMessageType { NewMessageUnread, @@ -88,29 +89,6 @@ private: class HistoryBlock; -struct DialogRow { - DialogRow(History *history = 0, DialogRow *prev = 0, DialogRow *next = 0, int32 pos = 0) : prev(prev), next(next), history(history), pos(pos), attached(0) { - } - - void paint(Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const; - - DialogRow *prev, *next; - History *history; - int32 pos; - void *attached; // for any attached data, for example View in contacts list -}; - -struct FakeDialogRow { - FakeDialogRow(HistoryItem *item) : _item(item), _cacheFor(0), _cache(st::dlgRichMinWidth) { - } - - void paint(Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const; - - HistoryItem *_item; - mutable const HistoryItem *_cacheFor; - mutable Text _cache; -}; - enum HistoryMediaType { MediaTypePhoto, MediaTypeVideo, @@ -215,7 +193,11 @@ enum AddToOverviewMethod { AddToOverviewBack, // when new messages slice was received and it is the last one, we index all media }; -struct DialogsIndexed; +namespace Dialogs { +class Row; +class IndexedList; +} // namespace Dialogs + class ChannelHistory; class History { public: @@ -283,22 +265,19 @@ public: void setLastMessage(HistoryItem *msg); void fixLastMessage(bool wasAtBottom); - typedef QMap ChatListLinksMap; void setChatsListDate(const QDateTime &date); - QPair adjustByPosInChatsList(DialogsIndexed &indexed); + QPair adjustByPosInChatsList(Dialogs::IndexedList *indexed); uint64 sortKeyInChatList() const { return _sortKeyInChatList; } bool inChatList() const { return !_chatListLinks.isEmpty(); } - int32 posInChatList() const { - return mainChatListLink()->pos; - } - DialogRow *addToChatList(DialogsIndexed &indexed); - void removeFromChatList(DialogsIndexed &indexed); + int posInChatList() const; + Dialogs::Row *addToChatList(Dialogs::IndexedList *indexed); + void removeFromChatList(Dialogs::IndexedList *indexed); void removeChatListEntryByLetter(QChar letter); - void addChatListEntryByLetter(QChar letter, DialogRow *row); + void addChatListEntryByLetter(QChar letter, Dialogs::Row *row); void updateChatListEntry() const; MsgId minMsgId() const; @@ -555,8 +534,8 @@ private: } Flags _flags; - ChatListLinksMap _chatListLinks; - DialogRow *mainChatListLink() const { + Dialogs::RowsByLetter _chatListLinks; + Dialogs::Row *mainChatListLink() const { auto it = _chatListLinks.constFind(0); t_assert(it != _chatListLinks.cend()); return it.value(); @@ -661,280 +640,6 @@ private: }; -enum DialogsSortMode { - DialogsSortByDate, - DialogsSortByName, - DialogsSortByAdd -}; - -struct DialogsList { - DialogsList(DialogsSortMode sortMode) : begin(&last), end(&last), sortMode(sortMode), count(0), current(&last) { - } - - void adjustCurrent(int32 y, int32 h) const { - 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 paint(Painter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel, bool onlyBackground) const { - adjustCurrent(hFrom, st::dlgHeight); - - DialogRow *drawFrom = current; - p.translate(0, drawFrom->pos * st::dlgHeight); - while (drawFrom != end && drawFrom->pos * st::dlgHeight < hTo) { - bool active = (drawFrom->history->peer == act) || (drawFrom->history->peer->migrateTo() && drawFrom->history->peer->migrateTo() == act); - bool selected = (drawFrom->history->peer == sel); - drawFrom->paint(p, w, active, selected, onlyBackground); - drawFrom = drawFrom->next; - p.translate(0, st::dlgHeight); - } - } - - DialogRow *rowAtY(int32 y, int32 h) const { - if (!count) return 0; - - int32 pos = (y > 0) ? (y / h) : 0; - adjustCurrent(y, h); - return (pos == current->pos) ? current : 0; - } - - DialogRow *addToEnd(History *history) { - DialogRow *result = new DialogRow(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 == DialogsSortByDate) { - adjustByPos(result); - } - return result; - } - - bool insertBefore(DialogRow *row, DialogRow *before) { - if (row == before) return false; - - if (current == row) current = row->prev; - - DialogRow *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 (DialogRow *n = row; n != updateTill; n = n->next) { - n->next->pos++; - row->pos--; - } - return true; - } - - bool insertAfter(DialogRow *row, DialogRow *after) { - if (row == after) return false; - - if (current == row) current = row->next; - - DialogRow *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 (DialogRow *n = updateFrom; n != row; n = n->next) { - n->pos--; - row->pos++; - } - return true; - } - - DialogRow *adjustByName(const PeerData *peer) { - if (sortMode != DialogsSortByName) return 0; - - RowByPeer::iterator i = rowByPeer.find(peer->id); - if (i == rowByPeer.cend()) return 0; - - DialogRow *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; - } - - DialogRow *addByName(History *history) { - if (sortMode != DialogsSortByName) return 0; - - DialogRow *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 adjustByPos(DialogRow *row) { - if (sortMode != DialogsSortByDate) return; - - DialogRow *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 del(const PeerId &peerId, DialogRow *replacedBy = 0); - - void remove(DialogRow *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 clear() { - while (begin != end) { - current = begin; - begin = begin->next; - delete current; - } - current = begin; - rowByPeer.clear(); - count = 0; - } - - ~DialogsList() { - clear(); - } - - DialogRow last; - DialogRow *begin, *end; - DialogsSortMode sortMode; - int32 count; - - typedef QHash RowByPeer; - RowByPeer rowByPeer; - - mutable DialogRow *current; // cache -}; - -struct DialogsIndexed { - DialogsIndexed(DialogsSortMode sortMode) : sortMode(sortMode), list(sortMode) { - } - - History::ChatListLinksMap addToEnd(History *history) { - History::ChatListLinksMap result; - DialogsList::RowByPeer::const_iterator i = list.rowByPeer.find(history->peer->id); - if (i == list.rowByPeer.cend()) { - result.insert(0, list.addToEnd(history)); - for (PeerData::NameFirstChars::const_iterator i = history->peer->chars.cbegin(), e = history->peer->chars.cend(); i != e; ++i) { - DialogsIndex::iterator j = index.find(*i); - if (j == index.cend()) { - j = index.insert(*i, new DialogsList(sortMode)); - } - result.insert(*i, j.value()->addToEnd(history)); - } - } - return result; - } - - DialogRow *addByName(History *history) { - DialogsList::RowByPeer::const_iterator i = list.rowByPeer.constFind(history->peer->id); - if (i != list.rowByPeer.cend()) { - return i.value(); - } - - DialogRow *res = list.addByName(history); - for (PeerData::NameFirstChars::const_iterator i = history->peer->chars.cbegin(), e = history->peer->chars.cend(); i != e; ++i) { - DialogsIndex::iterator j = index.find(*i); - if (j == index.cend()) { - j = index.insert(*i, new DialogsList(sortMode)); - } - j.value()->addByName(history); - } - return res; - } - - void adjustByPos(const History::ChatListLinksMap &links) { - for (History::ChatListLinksMap::const_iterator i = links.cbegin(), e = links.cend(); i != e; ++i) { - if (i.key() == QChar(0)) { - list.adjustByPos(i.value()); - } else { - DialogsIndex::iterator j = index.find(i.key()); - if (j != index.cend()) { - j.value()->adjustByPos(i.value()); - } - } - } - } - - void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); - - void del(const PeerData *peer, DialogRow *replacedBy = 0) { - if (list.del(peer->id, replacedBy)) { - for (PeerData::NameFirstChars::const_iterator i = peer->chars.cbegin(), e = peer->chars.cend(); i != e; ++i) { - DialogsIndex::iterator j = index.find(*i); - if (j != index.cend()) { - j.value()->del(peer->id, replacedBy); - } - } - } - } - - ~DialogsIndexed() { - clear(); - } - - void clear(); - - DialogsSortMode sortMode; - DialogsList list; - typedef QMap DialogsIndex; - DialogsIndex index; -}; - class HistoryBlock { public: HistoryBlock(History *hist) : y(0), height(0), history(hist), _indexInHistory(-1) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index ce5cf253f..a31d807f4 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -61,7 +61,7 @@ Result::Result(const Creator &creator) : _queryId(creator.queryId), _type(creato UniquePointer Result::create(uint64 queryId, const MTPBotInlineResult &mtpData) { using StringToTypeMap = QMap; - StaticNeverFreedPointer stringToTypeMap{ ([]() -> StringToTypeMap* { + static StaticNeverFreedPointer stringToTypeMap{ ([]() -> StringToTypeMap* { auto result = MakeUnique(); result->insert(qsl("photo"), Result::Type::Photo); result->insert(qsl("video"), Result::Type::Video); @@ -75,7 +75,7 @@ UniquePointer Result::create(uint64 queryId, const MTPBotInlineResult &m return result.release(); })() }; - auto getInlineResultType = [&stringToTypeMap](const MTPBotInlineResult &inlineResult) -> Type { + auto getInlineResultType = [](const MTPBotInlineResult &inlineResult) -> Type { QString type; switch (inlineResult.type()) { case mtpc_botInlineResult: type = qs(inlineResult.c_botInlineResult().vtype); break; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index dfacd1cc2..c9f83e15c 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1363,11 +1363,11 @@ void MainWidget::clearSelectedItems() { } } -DialogsIndexed &MainWidget::contactsList() { +Dialogs::IndexedList *MainWidget::contactsList() { return dialogs.contactsList(); } -DialogsIndexed &MainWidget::dialogsList() { +Dialogs::IndexedList *MainWidget::dialogsList() { return dialogs.dialogsList(); } @@ -2653,7 +2653,7 @@ QRect MainWidget::historyRect() const { return r; } -void MainWidget::dlgUpdated(DialogRow *row) { +void MainWidget::dlgUpdated(Dialogs::Row *row) { if (row) { dialogs.dlgUpdated(row); } else if (_peerInStack) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index a2cf9d6ec..e054e50ff 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -28,8 +28,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "playerwidget.h" class Window; -struct DialogRow; - class ApiWrap; class MainWidget; class ConfirmBox; @@ -39,6 +37,10 @@ class ProfileWidget; class OverviewWidget; class PlayerWidget; +namespace Dialogs { +class Row; +} // namespace Dialogs + class TopBarWidget : public TWidget { Q_OBJECT @@ -257,7 +259,7 @@ public: void createDialog(History *history); void removeDialog(History *history); - void dlgUpdated(DialogRow *row = 0); + void dlgUpdated(Dialogs::Row *row = nullptr); void dlgUpdated(History *row, MsgId msgId); void windowShown(); @@ -350,8 +352,8 @@ public: void deleteSelectedItems(); void clearSelectedItems(); - DialogsIndexed &contactsList(); - DialogsIndexed &dialogsList(); + Dialogs::IndexedList *contactsList(); + Dialogs::IndexedList *dialogsList(); void sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast, bool silent, WebPageId webPageId = 0); void saveRecentHashtags(const QString &text); @@ -477,7 +479,7 @@ signals: void peerUpdated(PeerData *peer); void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); void peerPhotoChanged(PeerData *peer); - void dialogRowReplaced(DialogRow *oldRow, DialogRow *newRow); + void dialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); void dialogsUpdated(); void stickersUpdated(); void savedGifsUpdated(); diff --git a/Telegram/SourceFiles/ui/flattextarea.cpp b/Telegram/SourceFiles/ui/flattextarea.cpp index 89a729e1e..d54753132 100644 --- a/Telegram/SourceFiles/ui/flattextarea.cpp +++ b/Telegram/SourceFiles/ui/flattextarea.cpp @@ -946,7 +946,7 @@ void FlatTextarea::keyPressEvent(QKeyEvent *e) { if (ctrl && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::Enter) { enterSubmit = true; } - if (!ctrl && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::CtrlEnter) { + if (!ctrl && !shift && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::CtrlEnter) { enterSubmit = true; } bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return); diff --git a/Telegram/SourceFiles/ui/text.cpp b/Telegram/SourceFiles/ui/text.cpp index 020b0c7f1..cea666df0 100644 --- a/Telegram/SourceFiles/ui/text.cpp +++ b/Telegram/SourceFiles/ui/text.cpp @@ -1341,7 +1341,7 @@ public: } const ClickHandlerPtr &link(int32 x, int32 y, int32 w, style::align align) { - StaticNeverFreedPointer zero(new ClickHandlerPtr()); + static StaticNeverFreedPointer zero(new ClickHandlerPtr()); _lnkX = x; _lnkY = y; diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro index dc6f4d3f9..14c8d4bfe 100644 --- a/Telegram/Telegram.pro +++ b/Telegram/Telegram.pro @@ -168,6 +168,9 @@ SOURCES += \ ./SourceFiles/boxes/sessionsbox.cpp \ ./SourceFiles/boxes/stickersetbox.cpp \ ./SourceFiles/boxes/usernamebox.cpp \ + ./SourceFiles/dialogs/dialogs_indexed_list.cpp \ + ./SourceFiles/dialogs/dialogs_layout.cpp \ + ./SourceFiles/dialogs/dialogs_list.cpp \ ./SourceFiles/inline_bots/inline_bot_layout_internal.cpp \ ./SourceFiles/inline_bots/inline_bot_layout_item.cpp \ ./SourceFiles/inline_bots/inline_bot_result.cpp \ @@ -274,6 +277,11 @@ HEADERS += \ ./SourceFiles/boxes/sessionsbox.h \ ./SourceFiles/boxes/stickersetbox.h \ ./SourceFiles/boxes/usernamebox.h \ + ./SourceFiles/dialogs/dialogs_common.h \ + ./SourceFiles/dialogs/dialogs_indexed_list.h \ + ./SourceFiles/dialogs/dialogs_layout.h \ + ./SourceFiles/dialogs/dialogs_list.h \ + ./SourceFiles/dialogs/dialogs_row.h \ ./SourceFiles/inline_bots/inline_bot_layout_internal.h \ ./SourceFiles/inline_bots/inline_bot_layout_item.h \ ./SourceFiles/inline_bots/inline_bot_result.h \ diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index db3c0f420..7e7bfeeee 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -1064,6 +1064,9 @@ + + + @@ -1223,6 +1226,11 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/basic_types.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\..\..\Libraries\breakpad\src" "-I.\ThirdParty\minizip" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" + + + + + diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 632fcaa22..24a016e94 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -61,6 +61,9 @@ {67311646-a8af-4626-976d-0a5733bf90e8} + + {405e59c2-0800-4f73-b975-1749c8c36e87} + @@ -1017,6 +1020,15 @@ serialize + + dialogs + + + dialogs + + + dialogs + @@ -1139,6 +1151,21 @@ serialize + + dialogs + + + dialogs + + + dialogs + + + dialogs + + + dialogs +