/* 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. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #include "stdafx.h" #include "lang.h" #include "newgroupbox.h" #include "mainwidget.h" #include "window.h" #include "confirmbox.h" NewGroupInner::NewGroupInner() : _contacts(&App::main()->contactsList()), _sel(0), _filteredSel(-1), _mouseSel(false), _selCount(0), _searching(false), _byUsernameSel(-1), _addContactLnk(this, lang(lng_add_contact_button)) { connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) { r->attached = 0; } _filter = qsl("a"); updateFilter(); for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) { r->attached = 0; } connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow *, DialogRow *)), this, SLOT(onDialogRowReplaced(DialogRow *, DialogRow *))); connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData *))); connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(peerUpdated(PeerData *))); connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *))); } void NewGroupInner::peerUpdated(PeerData *peer) { if (!peer->chat) { ContactsData::iterator i = _contactsData.find(peer->asUser()); if (i != _contactsData.cend()) { for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) { if (row->attached == i.value()) row->attached = 0; } if (!_filter.isEmpty()) { for (int32 j = 0, s = _filtered.size(); j < s; ++j) { if (_filtered[j]->attached == i.value()) _filtered[j]->attached = 0; } } delete i.value(); _contactsData.erase(i); } } parentWidget()->update(); } void NewGroupInner::loadProfilePhotos(int32 yFrom) { int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; MTP::clearLoaderPriorities(); if (yTo < 0) return; if (yFrom < 0) yFrom = 0; int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; if (_filter.isEmpty()) { if (_contacts->list.count) { _contacts->list.adjustCurrent(yFrom, rh); for ( DialogRow *preloadFrom = _contacts->list.current; preloadFrom != _contacts->list.end && preloadFrom->pos * rh < yTo; preloadFrom = preloadFrom->next ) { preloadFrom->history->peer->photo->load(); } } yFrom -= _contacts->list.count * rh + st::searchedBarHeight; yTo -= _contacts->list.count * rh + st::searchedBarHeight; int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; if (from < _byUsername.size()) { int32 to = (yTo / rh) + 1; if (to > _byUsername.size()) to = _byUsername.size(); for (; from < to; ++from) { _byUsername[from]->photo->load(); } } } else if (!_filtered.isEmpty()) { int32 from = yFrom / rh; if (from < 0) from = 0; if (from < _filtered.size()) { int32 to = (yTo / rh) + 1; if (to > _filtered.size()) to = _filtered.size(); for (; from < to; ++from) { _filtered[from]->history->peer->photo->load(); } } yFrom -= _filtered.size() * rh + st::searchedBarHeight; yTo -= _filtered.size() * rh + st::searchedBarHeight; from = (yFrom >= 0) ? (yFrom / rh) : 0; if (from < _byUsernameFiltered.size()) { int32 to = (yTo / rh) + 1; if (to > _byUsernameFiltered.size()) to = _byUsernameFiltered.size(); for (; from < to; ++from) { _byUsernameFiltered[from]->photo->load(); } } } } NewGroupInner::ContactData *NewGroupInner::contactData(DialogRow *row) { ContactData *data = (ContactData*)row->attached; if (!data) { UserData *user = row->history->peer->asUser(); ContactsData::const_iterator i = _contactsData.constFind(user); if (i == _contactsData.cend()) { _contactsData.insert(user, data = new ContactData()); data->check = false; data->name.setText(st::profileListNameFont, user->name, _textNameOptions); data->online = App::onlineText(user, _time); } else { data = i.value(); } row->attached = data; } return data; } void NewGroupInner::paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel) { int32 left = st::profileListPadding.width(); if (_selCount >= cMaxGroupCount() && !data->check) { sel = false; } if (sel || data->check) { p.fillRect(0, 0, width(), 2 * st::profileListPadding.height() + st::profileListPhotoSize, (data->check ? st::profileActiveBG : st::profileHoverBG)->b); } p.drawPixmap(left, st::profileListPadding.height(), user->photo->pix(st::profileListPhotoSize)); if (data->check) { p.setPen(st::white->p); } else { p.setPen(st::profileListNameColor->p); } data->name.drawElided(p, left + st::profileListPhotoSize + st::participantDelta, st::profileListNameTop, width() - st::profileListPadding.width() - st::profileListPhotoSize - st::profileListPadding.width() - st::participantDelta - st::scrollDef.width - st::profileCheckRect.pxWidth()); if (sel || data->check) { p.drawPixmap(QPoint(width() - st::profileCheckRect.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::profileCheckRect.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), (data->check ? st::profileCheckActiveRect : st::profileCheckRect)); } bool uname = (data->online.at(0) == '@'); p.setFont(st::profileSubFont->f); if (uname && !data->check && !_lastQuery.isEmpty() && user->username.startsWith(_lastQuery, Qt::CaseInsensitive)) { int32 availw = width() - (left + st::profileListPhotoSize + st::profileListPadding.width() * 2); QString first = '@' + user->username.mid(0, _lastQuery.size()), second = user->username.mid(_lastQuery.size()); int32 w = st::profileSubFont->m.width(first); if (w >= availw || second.isEmpty()) { p.setPen(st::profileOnlineColor->p); p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, st::profileSubFont->m.elidedText(first, Qt::ElideRight, availw)); } else { p.setPen(st::profileOnlineColor->p); p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, first); p.setPen(st::profileOfflineColor->p); p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width() + w, st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, st::profileSubFont->m.elidedText(second, Qt::ElideRight, availw - w)); } } else { if (data->check) { p.setPen(st::white->p); } else { p.setPen(((uname || App::onlineColorUse(user->onlineTill, _time)) ? st::profileOnlineColor : st::profileOfflineColor)->p); } p.drawText(left + st::profileListPhotoSize + st::participantDelta, st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online); } } void NewGroupInner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); QPainter p(this); _time = unixtime(); p.fillRect(r, st::white->b); int32 yFrom = r.top(), yTo = r.bottom(); int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; if (_filter.isEmpty()) { if (_contacts->list.count || !_byUsername.isEmpty()) { if (_contacts->list.count) { _contacts->list.adjustCurrent(yFrom, rh); DialogRow *drawFrom = _contacts->list.current; p.translate(0, drawFrom->pos * rh); while (drawFrom != _contacts->list.end && drawFrom->pos * rh < r.bottom()) { paintDialog(p, drawFrom->history->peer->asUser(), contactData(drawFrom), (drawFrom == _sel)); p.translate(0, rh); drawFrom = drawFrom->next; } } if (!_byUsername.isEmpty()) { p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b); p.setFont(st::searchedBarFont->f); p.setPen(st::searchedBarColor->p); p.drawText(QRect(0, 0, width(), st::searchedBarHeight), lang(lng_search_global_results), style::al_center); p.translate(0, st::searchedBarHeight); yFrom -= _contacts->list.count * rh + st::searchedBarHeight; yTo -= _contacts->list.count * rh + st::searchedBarHeight; int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; if (from < _byUsername.size()) { int32 to = (yTo / rh) + 1; if (to > _byUsername.size()) to = _byUsername.size(); p.translate(0, from * rh); for (; from < to; ++from) { paintDialog(p, _byUsername[from], d_byUsername[from], (_byUsernameSel == from)); p.translate(0, rh); } } } } else { p.setFont(st::noContactsFont->f); p.setPen(st::noContactsColor->p); p.drawText(QRect(0, 0, width(), st::noContactsHeight - ((cContactsReceived() && !_searching) ? st::noContactsFont->height : 0)), lang((cContactsReceived() && !_searching) ? lng_no_contacts : lng_contacts_loading), style::al_center); } } else { if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) { p.setFont(st::noContactsFont->f); p.setPen(st::noContactsColor->p); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang((cContactsReceived() && !_searching) ? lng_no_contacts : lng_contacts_not_found), style::al_center); } else { if (!_filtered.isEmpty()) { int32 from = yFrom / rh; if (from < 0) from = 0; if (from < _filtered.size()) { int32 to = (r.bottom() / rh) + 1; if (to > _filtered.size()) to = _filtered.size(); p.translate(0, from * rh); for (; from < to; ++from) { paintDialog(p, _filtered[from]->history->peer->asUser(), contactData(_filtered[from]), (_filteredSel == from)); p.translate(0, rh); } } } if (!_byUsernameFiltered.isEmpty()) { p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b); p.setFont(st::searchedBarFont->f); p.setPen(st::searchedBarColor->p); p.drawText(QRect(0, 0, width(), st::searchedBarHeight), lang(lng_search_global_results), style::al_center); p.translate(0, st::searchedBarHeight); yFrom -= _filtered.size() * rh + st::searchedBarHeight; yTo -= _filtered.size() * rh + st::searchedBarHeight; int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; if (from < _byUsernameFiltered.size()) { int32 to = (yTo / rh) + 1; if (to > _byUsernameFiltered.size()) to = _byUsernameFiltered.size(); p.translate(0, from * rh); for (; from < to; ++from) { paintDialog(p, _byUsernameFiltered[from], d_byUsernameFiltered[from], (_byUsernameSel == from)); p.translate(0, rh); } } } } } } void NewGroupInner::enterEvent(QEvent *e) { setMouseTracking(true); } void NewGroupInner::leaveEvent(QEvent *e) { setMouseTracking(false); if (_sel || _filteredSel >= 0 || _byUsernameSel >= 0) { _sel = 0; _filteredSel = _byUsernameSel = -1; parentWidget()->update(); } } void NewGroupInner::mouseMoveEvent(QMouseEvent *e) { _mouseSel = true; _lastMousePos = e->globalPos(); updateSel(); } void NewGroupInner::mousePressEvent(QMouseEvent *e) { _mouseSel = true; _lastMousePos = e->globalPos(); updateSel(); if (e->button() == Qt::LeftButton) { chooseParticipant(); } } void NewGroupInner::changeCheckState(DialogRow *row) { changeCheckState(contactData(row)); } void NewGroupInner::changeCheckState(ContactData *data) { if (data->check) { data->check = false; --_selCount; } else if (_selCount < cMaxGroupCount()) { data->check = true; ++_selCount; } } void NewGroupInner::chooseParticipant() { int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from; if (_filter.isEmpty()) { if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) { changeCheckState(d_byUsername[_byUsernameSel]); } else { if (!_sel) return; changeCheckState(_sel); } } else { if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) { changeCheckState(d_byUsernameFiltered[_byUsernameSel]); ContactData *moving = d_byUsernameFiltered[_byUsernameSel]; int32 i = 0, l = d_byUsername.size(); for (; i < l; ++i) { if (d_byUsername[i] == moving) { break; } } if (i == l) { d_byUsername.push_back(moving); _byUsername.push_back(_byUsernameFiltered[_byUsernameSel]); for (i = 0, l = _byUsernameDatas.size(); i < l;) { if (_byUsernameDatas[i] == moving) { _byUsernameDatas.removeAt(i); --l; } else { ++i; } } } } else { if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return; changeCheckState(_filtered[_filteredSel]); } emit selectAllQuery(); } parentWidget()->update(); } void NewGroupInner::peopleReceived(const QString &query, const QVector &people) { _lastQuery = query.toLower().trimmed(); if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1); int32 already = _byUsernameFiltered.size(); _byUsernameFiltered.reserve(already + people.size()); d_byUsernameFiltered.reserve(already + people.size()); for (QVector::const_iterator i = people.cbegin(), e = people.cend(); i != e; ++i) { int32 uid = i->c_contactFound().vuser_id.v, j = 0; for (; j < already; ++j) { if (_byUsernameFiltered[j]->id == uid) break; } if (j == already) { UserData *u = App::user(uid); ContactData *d = new ContactData(); _byUsernameDatas.push_back(d); d->check = false; d->name.setText(st::profileListNameFont, u->name, _textNameOptions); d->online = '@' + u->username; _byUsernameFiltered.push_back(u); d_byUsernameFiltered.push_back(d); } } _searching = false; refresh(); } void NewGroupInner::refresh() { int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; if (_filter.isEmpty()) { if (_contacts->list.count || !_byUsername.isEmpty()) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); resize(width(), (_contacts->list.count * rh) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * rh))); } else { if (cContactsReceived()) { if (_addContactLnk.isHidden()) _addContactLnk.show(); } else { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); } resize(width(), st::noContactsHeight); } } else { if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); resize(width(), st::noContactsHeight); } else { resize(width(), (_filtered.size() * rh) + (_byUsernameFiltered.isEmpty() ? 0 : (st::searchedBarHeight + _byUsernameFiltered.size() * rh))); } } } void NewGroupInner::updateSel() { if (!_mouseSel) return; QPoint p(mapFromGlobal(_lastMousePos)); bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; if (_filter.isEmpty()) { DialogRow *newSel = (in && (p.y() >= 0) && (p.y() < _contacts->list.count * rh)) ? _contacts->list.rowAtY(p.y(), rh) : 0; int32 byUsernameSel = (in && p.y() >= _contacts->list.count * rh + st::searchedBarHeight) ? ((p.y() - _contacts->list.count * rh - st::searchedBarHeight) / rh) : -1; if (byUsernameSel >= _byUsername.size()) byUsernameSel = -1; if (newSel != _sel || byUsernameSel != _byUsernameSel) { _sel = newSel; _byUsernameSel = byUsernameSel; parentWidget()->update(); } } else { int32 newFilteredSel = (in && p.y() >= 0 && p.y() < _filtered.size() * rh) ? (p.y() / rh) : -1; int32 byUsernameSel = (in && p.y() >= _filtered.size() * rh + st::searchedBarHeight) ? ((p.y() - _filtered.size() * rh - st::searchedBarHeight) / rh) : -1; if (byUsernameSel >= _byUsernameFiltered.size()) byUsernameSel = -1; if (newFilteredSel != _filteredSel || byUsernameSel != _byUsernameSel) { _filteredSel = newFilteredSel; _byUsernameSel = byUsernameSel; parentWidget()->update(); } } } void NewGroupInner::updateFilter(QString filter) { _lastQuery = filter.toLower().trimmed(); filter = textSearchKey(filter); QStringList f; if (!filter.isEmpty()) { QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts); int l = filterList.size(); f.reserve(l); for (int i = 0; i < l; ++i) { QString filterName = filterList[i].trimmed(); if (filterName.isEmpty()) continue; f.push_back(filterName); } filter = f.join(' '); } if (_filter != filter) { int32 rh = (st::profileListPhotoSize + st::profileListPadding.height() * 2); _filter = filter; _byUsernameFiltered.clear(); d_byUsernameFiltered.clear(); for (int i = 0, l = _byUsernameDatas.size(); i < l; ++i) { delete _byUsernameDatas[i]; } _byUsernameDatas.clear(); if (_filter.isEmpty()) { _sel = 0; if (_contacts->list.count) { _sel = _contacts->list.begin; } if (!_sel && !_byUsername.isEmpty()) { _byUsernameSel = 0; } refresh(); } else { if (!_addContactLnk.isHidden()) _addContactLnk.hide(); QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; _filtered.clear(); if (!f.isEmpty()) { DialogsList *dialogsToFilter = 0; if (_contacts->list.count) { for (fi = fb; fi != fe; ++fi) { DialogsIndexed::DialogsIndex::iterator i = _contacts->index.find(fi->at(0)); if (i == _contacts->index.cend()) { dialogsToFilter = 0; break; } if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) { dialogsToFilter = i.value(); } } } 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); PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; for (fi = fb; fi != fe; ++fi) { QString filterName(*fi); for (ni = nb; ni != ne; ++ni) { if (ni->startsWith(*fi)) { break; } } if (ni == ne) { break; } } if (fi == fe) { i->attached = 0; _filtered.push_back(i); } } } _byUsernameFiltered.reserve(_byUsername.size()); d_byUsernameFiltered.reserve(d_byUsername.size()); for (int32 i = 0, l = _byUsername.size(); i < l; ++i) { const PeerData::Names &names(_byUsername[i]->names); PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; for (fi = fb; fi != fe; ++fi) { QString filterName(*fi); for (ni = nb; ni != ne; ++ni) { if (ni->startsWith(*fi)) { break; } } if (ni == ne) { break; } } if (fi == fe) { _byUsernameFiltered.push_back(_byUsername[i]); d_byUsernameFiltered.push_back(d_byUsername[i]); } } } _filteredSel = _filtered.isEmpty() ? -1 : 0; _byUsernameSel = (_filtered.isEmpty() && !_byUsernameFiltered.isEmpty()) ? 0 : -1; refresh(); _searching = true; emit searchByUsername(); } if (parentWidget()) parentWidget()->update(); loadProfilePhotos(0); } } void NewGroupInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *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! if (newRow) { *i = newRow; ++i; } else { i = _filtered.erase(i); } } else { ++i; } } if (_filteredSel >= _filtered.size()) { _filteredSel = -1; } } else { if (_sel == oldRow) { _sel = newRow; } } _mouseSel = false; int32 rh = (st::profileListPhotoSize + st::profileListPadding.height() * 2); int32 newh = (_filter.isEmpty() ? _contacts->list.count : _filtered.size()) * rh; resize(width(), newh); } NewGroupInner::~NewGroupInner() { for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { delete *i; } for (ByUsernameDatas::const_iterator i = d_byUsername.cbegin(), e = d_byUsername.cend(); i != e; ++i) { delete *i; } for (ByUsernameDatas::const_iterator i = _byUsernameDatas.cbegin(), e = _byUsernameDatas.cend(); i != e; ++i) { delete *i; } } void NewGroupInner::resizeEvent(QResizeEvent *e) { _addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); } void NewGroupInner::selectSkip(int32 dir) { _mouseSel = false; int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, origDir = dir; if (_filter.isEmpty()) { int cur = 0; if (_sel) { for (DialogRow *i = _contacts->list.begin; i != _sel; i = i->next) { ++cur; } } else { cur = (_byUsernameSel >= 0) ? (_contacts->list.count + _byUsernameSel) : -1; } cur += dir; if (cur <= 0) { _sel = _contacts->list.count ? _contacts->list.begin : 0; _byUsernameSel = (!_contacts->list.count && !_byUsername.isEmpty()) ? 0 : -1; } else if (cur >= _contacts->list.count) { _sel = 0; _byUsernameSel = cur - _contacts->list.count; if (_byUsernameSel >= _byUsername.size()) _byUsernameSel = _byUsername.size() - 1; } else { for (_sel = _contacts->list.begin; cur; _sel = _sel->next) { --cur; } _byUsernameSel = -1; } if (_sel) { emit mustScrollTo(_sel->pos * rh, (_sel->pos + 1) * rh); } else if (_byUsernameSel >= 0) { emit mustScrollTo((_contacts->list.count + _byUsernameSel) * rh + st::searchedBarHeight, (_contacts->list.count + _byUsernameSel + 1) * rh + st::searchedBarHeight); } } else { int cur = (_filteredSel >= 0) ? _filteredSel : ((_byUsernameSel >= 0) ? (_filtered.size() + _byUsernameSel) : -1); cur += dir; if (cur <= 0) { _filteredSel = _filtered.isEmpty() ? -1 : 0; _byUsernameSel = (_filtered.isEmpty() && !_byUsernameFiltered.isEmpty()) ? 0 : -1; } else if (cur >= _filtered.size()) { _filteredSel = -1; _byUsernameSel = cur - _filtered.size(); if (_byUsernameSel >= _byUsernameFiltered.size()) _byUsernameSel = _byUsernameFiltered.size() - 1; } else { _filteredSel = cur; _byUsernameSel = -1; } if (_filteredSel >= 0) { emit mustScrollTo(_filteredSel * rh, (_filteredSel + 1) * rh); } else if (_byUsernameSel >= 0) { int skip = _filtered.size() * rh + st::searchedBarHeight; emit mustScrollTo(skip + _byUsernameSel * rh, skip + (_byUsernameSel + 1) * rh); } } parentWidget()->update(); } void NewGroupInner::selectSkipPage(int32 h, int32 dir) { int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; int32 points = h / rh; if (!points) return; selectSkip(points * dir); } QVector NewGroupInner::selectedInputs() { QVector result; result.reserve(_contactsData.size()); for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { if (i.value()->check) { result.push_back(i.key()->inputUser); } } for (int32 i = 0, l = _byUsername.size(); i < l; ++i) { if (d_byUsername[i]->check) { result.push_back(_byUsername[i]->inputUser); } } return result; } PeerData *NewGroupInner::selectedUser() { for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { if (i.value()->check) { return i.key(); } } for (int32 i = 0, l = _byUsername.size(); i < l; ++i) { if (d_byUsername[i]->check) { return _byUsername[i]; } } return 0; } NewGroupBox::NewGroupBox() : _scroll(this, st::newGroupScroll), _inner(), _filter(this, st::contactsFilter, lang(lng_participant_filter)), _next(this, lang(lng_create_group_next), st::btnSelectDone), _cancel(this, lang(lng_cancel), st::btnSelectCancel), _hiding(false), a_opacity(0, 1) { _width = st::participantWidth; _height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom(); if (_height > st::participantMaxHeight) _height = st::participantMaxHeight; resize(_width, _height); _scroll.setWidget(&_inner); _scroll.setFocusPolicy(Qt::NoFocus); connect(&_next, SIGNAL(clicked()), this, SLOT(onNext())); connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(closed())); connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSel())); connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); connect(&_filter, SIGNAL(cancelled()), this, SLOT(onClose())); connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int))); connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll())); connect(&_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername())); _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); showAll(); _cache = myGrab(this, rect()); hideAll(); } bool NewGroupBox::onSearchByUsername(bool searchCache) { QString q = _filter.text().trimmed(); if (q.isEmpty()) { if (_peopleRequest) { _peopleRequest = 0; } return true; } if (q.size() >= MinUsernameLength) { if (searchCache) { PeopleCache::const_iterator i = _peopleCache.constFind(q); if (i != _peopleCache.cend()) { _peopleQuery = q; _peopleRequest = 0; peopleReceived(i.value(), 0); return true; } } else if (_peopleQuery != q) { _peopleQuery = q; _peopleFull = false; _peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&NewGroupBox::peopleReceived), rpcFail(&NewGroupBox::peopleFailed)); _peopleQueries.insert(_peopleRequest, _peopleQuery); } } return false; } void NewGroupBox::onNeedSearchByUsername() { if (!onSearchByUsername(true)) { _searchTimer.start(AutoSearchTimeout); } } void NewGroupBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId req) { QString q = _peopleQuery; PeopleQueries::iterator i = _peopleQueries.find(req); if (i != _peopleQueries.cend()) { q = i.value(); _peopleCache[q] = result; _peopleQueries.erase(i); } if (_peopleRequest == req) { switch (result.type()) { case mtpc_contacts_found: { App::feedUsers(result.c_contacts_found().vusers); _inner.peopleReceived(q, result.c_contacts_found().vresults.c_vector().v); } break; } _peopleRequest = 0; _inner.updateSel(); onScroll(); } } bool NewGroupBox::peopleFailed(const RPCError &error, mtpRequestId req) { if (_peopleRequest == req) { _peopleRequest = 0; _peopleFull = true; } return true; } void NewGroupBox::hideAll() { _filter.hide(); _scroll.hide(); _next.hide(); _cancel.hide(); } void NewGroupBox::showAll() { _filter.show(); _scroll.show(); _next.show(); _cancel.show(); } void NewGroupBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { onClose(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { _inner.chooseParticipant(); } else if (_filter.hasFocus()) { if (e->key() == Qt::Key_Down) { _inner.selectSkip(1); } else if (e->key() == Qt::Key_Up) { _inner.selectSkip(-1); } else if (e->key() == Qt::Key_PageDown) { _inner.selectSkipPage(_scroll.height(), 1); } else if (e->key() == Qt::Key_PageUp) { _inner.selectSkipPage(_scroll.height(), -1); } else { e->ignore(); } } else { e->ignore(); } } void NewGroupBox::parentResized() { QSize s = parentWidget()->size(); _height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom(); if (_height > st::participantMaxHeight) _height = st::participantMaxHeight; setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height); update(); } void NewGroupBox::paintEvent(QPaintEvent *e) { QPainter p(this); if (_cache.isNull()) { if (!_hiding || a_opacity.current() > 0.01) { // fill bg p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b); // paint shadows p.fillRect(0, st::participantFilter.height, _width, st::scrollDef.topsh, st::scrollDef.shColor->b); p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b); // paint button sep p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b); // draw box title / text p.setPen(st::black->p); p.setFont(st::addContactTitleFont->f); p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_create_new_group)); } } else { p.setOpacity(a_opacity.current()); p.drawPixmap(0, 0, _cache); } } void NewGroupBox::resizeEvent(QResizeEvent *e) { LayeredWidget::resizeEvent(e); _filter.move(st::newGroupNamePadding.left(), st::contactsAdd.height + st::newGroupNamePadding.top()); _inner.resize(_width, _inner.height()); _scroll.resize(_width, _height - st::contactsAdd.height - st::newGroupNamePadding.top() - _filter.height() - st::newGroupNamePadding.bottom() - _cancel.height()); _scroll.move(0, _filter.y() + _filter.height() + st::newGroupNamePadding.bottom()); _next.move(width() - _next.width(), _height - _next.height()); _cancel.move(0, _height - _cancel.height()); } void NewGroupBox::animStep(float64 dt) { if (dt >= 1) { a_opacity.finish(); _cache = QPixmap(); if (!_hiding) { showAll(); _filter.setFocus(); } } else { a_opacity.update(dt, anim::linear); } update(); } void NewGroupBox::startHide() { _hiding = true; if (_cache.isNull()) { _cache = myGrab(this, rect()); hideAll(); } a_opacity.start(0); } void NewGroupBox::onFilterUpdate() { _scroll.scrollToY(0); _inner.updateFilter(_filter.text()); } void NewGroupBox::onClose() { emit closed(); } void NewGroupBox::onNext() { MTPVector users(MTP_vector(_inner.selectedInputs())); const QVector &v(users.c_vector().v); if (v.isEmpty()) { _filter.setFocus(); _filter.notaBene(); } else if (v.size() == 1) { App::main()->showPeer(_inner.selectedUser()->id); } else { App::wnd()->replaceLayer(new CreateGroupBox(users)); } } void NewGroupBox::onScroll() { _inner.loadProfilePhotos(_scroll.scrollTop()); } NewGroupBox::~NewGroupBox() { } CreateGroupBox::CreateGroupBox(const MTPVector &users) : _users(users), _createRequestId(0), _name(this, st::newGroupName, lang(lng_dlg_new_group_name)), _create(this, lang(lng_dlg_create_group), st::btnSelectDone), _cancel(this, lang(lng_cancel), st::btnSelectCancel), _hiding(false), a_opacity(0, 1) { _width = st::addContactWidth; _height = st::addContactTitleHeight + st::addContactPadding.top() + _name.height() + st::addContactPadding.bottom() + _create.height(); _name.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _name.height()); int32 buttonTop = _name.y() + _name.height() + st::addContactPadding.bottom(); _cancel.move(0, buttonTop); _create.move(_width - _create.width(), buttonTop); connect(&_create, SIGNAL(clicked()), this, SLOT(onCreate())); connect(&_cancel, SIGNAL(clicked()), this, SLOT(onCancel())); resize(_width, _height); showAll(); _cache = myGrab(this, rect()); hideAll(); } void CreateGroupBox::hideAll() { _name.hide(); _cancel.hide(); _create.hide(); } void CreateGroupBox::showAll() { _name.show(); _cancel.show(); _create.show(); } void CreateGroupBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { if (_name.hasFocus()) { if (_name.text().trimmed().isEmpty()) { _name.setFocus(); _name.notaBene(); } else { onCreate(); } } } else if (e->key() == Qt::Key_Escape) { onCancel(); } } void CreateGroupBox::parentResized() { QSize s = parentWidget()->size(); setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height); update(); } void CreateGroupBox::paintEvent(QPaintEvent *e) { QPainter p(this); if (_cache.isNull()) { if (!_hiding || a_opacity.current() > 0.01) { // fill bg p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b); // paint shadows p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b); p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b); // paint button sep p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b); // draw box title / text p.setPen(st::black->p); p.setFont(st::addContactTitleFont->f); p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_create_group_title)); } } else { p.setOpacity(a_opacity.current()); p.drawPixmap(0, 0, _cache); } } void CreateGroupBox::animStep(float64 dt) { if (dt >= 1) { a_opacity.finish(); _cache = QPixmap(); if (!_hiding) { showAll(); _name.setFocus(); } } else { a_opacity.update(dt, anim::linear); } update(); } void CreateGroupBox::onCreate() { if (_createRequestId) return; QString name = _name.text(); if (name.isEmpty()) { _name.setFocus(); _name.notaBene(); return; } _create.setDisabled(true); _name.setDisabled(true); _createRequestId = MTP::send(MTPmessages_CreateChat(_users, MTP_string(_name.text())), rpcDone(&CreateGroupBox::created), rpcFail(&CreateGroupBox::failed)); } void CreateGroupBox::created(const MTPmessages_StatedMessage &result) { App::main()->sentFullDataReceived(0, result); const QVector *d = 0; switch (result.type()) { case mtpc_messages_statedMessage: { d = &result.c_messages_statedMessage().vchats.c_vector().v; } break; case mtpc_messages_statedMessageLink: { d = &result.c_messages_statedMessageLink().vchats.c_vector().v; } break; } App::wnd()->hideLayer(); PeerId peerId = 0; if (d && !d->isEmpty()) { switch (d->first().type()) { case mtpc_chat: peerId = App::peerFromChat(d->first().c_chat().vid); break; case mtpc_chatForbidden: peerId = App::peerFromChat(d->first().c_chatForbidden().vid); break; case mtpc_chatEmpty: peerId = App::peerFromChat(d->first().c_chatEmpty().vid); break; } } if (peerId) { App::main()->showPeer(peerId); } } bool CreateGroupBox::failed(const RPCError &e) { _createRequestId = 0; if (e.type() == "NO_CHAT_TITLE") { _name.setFocus(); return true; } else if (e.type() == "USERS_TOO_FEW") { emit closed(); return true; } return false; } void CreateGroupBox::onCancel() { emit closed(); } void CreateGroupBox::startHide() { _hiding = true; if (_cache.isNull()) { _cache = myGrab(this, rect()); hideAll(); } a_opacity.start(0); } CreateGroupBox::~CreateGroupBox() { }