add users to groups by usernames, copy username from context menu in profile, 0.7.25.dev version

This commit is contained in:
John Preston 2015-03-25 18:42:15 +03:00
parent 489b151d49
commit 8c7a35c973
18 changed files with 963 additions and 245 deletions

View file

@ -1,9 +1,9 @@
@echo OFF
set "AppVersion=7024"
set "AppVersionStrSmall=0.7.24"
set "AppVersionStr=0.7.24"
set "AppVersionStrFull=0.7.24.0"
set "AppVersion=7025"
set "AppVersionStrSmall=0.7.25"
set "AppVersionStr=0.7.25"
set "AppVersionStrFull=0.7.25.0"
set "DevChannel=1"
if %DevChannel% neq 0 goto preparedev

View file

@ -313,6 +313,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Next";
"lng_create_group_title" = "New Group";
"lng_failed_add_participant" = "Could not add user. Try again later.";
"lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
@ -489,8 +491,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
"lng_new_version7022" = "— Reply to specific messages in groups\n— Mention @usernames in groups to notify multiple users";
"lng_new_version7022_appstore" = "— Reply to specific messages in groups\n— Mention @usernames in groups to notify multiple users";
"lng_new_version7026" = "— Add a comment before forwarded messages\n— Hashtags suggestions in new message and search fields (based on recent searches)\n— Button to jump back to a reply after viewing the original message\n— Add people to groups by username";
"lng_new_version7026_appstore" = "— Add a comment before forwarded messages\n— Hashtags suggestions in new message and search fields (based on recent searches)\n— Button to jump back to a reply after viewing the original message\n— Add people to groups by username";
"lng_menu_insert_unicode" = "Insert Unicode control character";

View file

@ -653,10 +653,10 @@ void Application::checkMapVersion() {
psRegisterCustomScheme();
if (Local::oldMapVersion()) {
QString versionFeatures;
if (DevChannel && Local::oldMapVersion() < 7024) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Forward messages with comment or sticker before them\n\xe2\x80\x94 Recent hashtags autocomplete in message and search fields\n\xe2\x80\x94 Move from reply to original message and back");
} else if (!DevChannel && Local::oldMapVersion() < 7023) {
versionFeatures = lang(lng_new_version7022).trimmed().replace('@', qsl("@") + QChar(0x200D));
if (DevChannel && Local::oldMapVersion() < 7025) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Add people to groups by username\n\xe2\x80\x94 Copy @username from profile context menu").replace('@', qsl("@") + QChar(0x200D));
} else if (!DevChannel && Local::oldMapVersion() < 7026) {
versionFeatures = lang(lng_new_version7026).trimmed();
}
if (!versionFeatures.isEmpty()) {
versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog"));

View file

@ -28,6 +28,8 @@ _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()));
@ -101,6 +103,16 @@ void AddParticipantInner::loadProfilePhotos(int32 yFrom) {
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;
@ -112,6 +124,16 @@ void AddParticipantInner::loadProfilePhotos(int32 yFrom) {
_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();
}
}
}
}
@ -122,7 +144,7 @@ AddParticipantInner::ContactData *AddParticipantInner::contactData(DialogRow *ro
ContactsData::const_iterator i = _contactsData.constFind(user);
if (i == _contactsData.cend()) {
_contactsData.insert(user, data = new ContactData());
data->inchat = _chat->participants.constFind(user) != _chat->participants.cend();
data->inchat = _chat->participants.contains(user);
data->check = false;
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
data->online = App::onlineText(user, _time);
@ -134,12 +156,9 @@ AddParticipantInner::ContactData *AddParticipantInner::contactData(DialogRow *ro
return data;
}
void AddParticipantInner::paintDialog(QPainter &p, DialogRow *row, bool sel) {
void AddParticipantInner::paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel) {
int32 left = st::profileListPadding.width();
UserData *user = row->history->peer->asUser();
ContactData *data = contactData(row);
if (data->inchat || data->check || _selCount + _chat->count >= cMaxGroupCount()) {
sel = false;
}
@ -161,13 +180,29 @@ void AddParticipantInner::paintDialog(QPainter &p, DialogRow *row, bool sel) {
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 (data->inchat || data->check) {
p.setPen(st::white->p);
if (uname && !data->inchat && !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 {
p.setPen((App::onlineColorUse(user->onlineTill, _time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
if (data->inchat || 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::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online);
}
p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online);
}
void AddParticipantInner::paintEvent(QPaintEvent *e) {
@ -177,40 +212,85 @@ void AddParticipantInner::paintEvent(QPaintEvent *e) {
_time = unixtime();
p.fillRect(r, st::white->b);
int32 yFrom = r.top();
int32 yFrom = r.top(), yTo = r.bottom();
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
_contacts->list.adjustCurrent(yFrom, rh);
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, (drawFrom == _sel));
p.translate(0, rh);
drawFrom = drawFrom->next;
DialogRow *drawFrom = _contacts->list.current;
p.translate(0, drawFrom->pos * rh);
while (drawFrom != _contacts->list.end && drawFrom->pos * rh < yTo) {
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() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
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()) {
if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_not_found), style::al_center);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang((cContactsReceived() && !_searching) ? lng_no_contacts : lng_contacts_loading), style::al_center);
} else {
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();
if (!_filtered.isEmpty()) {
int32 from = (yFrom >= 0) ? (yFrom / rh) : 0;
if (from < _filtered.size()) {
int32 to = (yTo / rh) + 1;
if (to > _filtered.size()) to = _filtered.size();
p.translate(0, from * rh);
for (; from < to; ++from) {
paintDialog(p, _filtered[from], (_filteredSel == from));
p.translate(0, rh);
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);
}
}
}
}
@ -223,7 +303,11 @@ void AddParticipantInner::enterEvent(QEvent *e) {
void AddParticipantInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
updateSel();
if (_sel || _filteredSel >= 0 || _byUsernameSel >= 0) {
_sel = 0;
_filteredSel = _byUsernameSel = -1;
parentWidget()->update();
}
}
void AddParticipantInner::mouseMoveEvent(QMouseEvent *e) {
@ -245,30 +329,112 @@ void AddParticipantInner::chooseParticipant() {
_time = unixtime();
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
if (_filter.isEmpty()) {
if (!_sel || contactData(_sel)->inchat) return;
changeCheckState(_sel);
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) {
if (d_byUsername[_byUsernameSel]->inchat) return;
changeCheckState(d_byUsername[_byUsernameSel]);
} else {
if (!_sel || contactData(_sel)->inchat) return;
changeCheckState(_sel);
}
} else {
if (_filteredSel < 0 || _filteredSel >= _filtered.size() || contactData(_filtered[_filteredSel])->inchat) return;
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) {
if (d_byUsernameFiltered[_byUsernameSel]->inchat) return;
changeCheckState(d_byUsernameFiltered[_byUsernameSel]);
DialogRow *row = _filtered[_filteredSel];
changeCheckState(row);
PeerData *peer = row->history->peer;
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() || contactData(_filtered[_filteredSel])->inchat) return;
changeCheckState(_filtered[_filteredSel]);
}
emit selectAllQuery();
}
parentWidget()->update();
}
void AddParticipantInner::changeCheckState(DialogRow *row) {
if (contactData(row)->check) {
contactData(row)->check = false;
changeCheckState(contactData(row));
}
void AddParticipantInner::changeCheckState(ContactData *data) {
if (data->check) {
data->check = false;
--_selCount;
} else if (_selCount + _chat->count < cMaxGroupCount()) {
contactData(row)->check = true;
data->check = true;
++_selCount;
}
}
void AddParticipantInner::peopleReceived(const QString &query, const QVector<MTPContactFound> &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<MTPContactFound>::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->inchat = _chat->participants.contains(u);
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 AddParticipantInner::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)));
}
}
}
ChatData *AddParticipantInner::chat() {
return _chat;
}
@ -281,6 +447,11 @@ QVector<UserData*> AddParticipantInner::selected() {
result.push_back(i.key());
}
}
for (int32 i = 0, l = _byUsername.size(); i < l; ++i) {
if (d_byUsername[i]->check) {
result.push_back(_byUsername[i]);
}
}
return result;
}
@ -288,23 +459,31 @@ void AddParticipantInner::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 = rect().contains(p) ? _contacts->list.rowAtY(p.y(), rh) : 0;
if (newSel != _sel) {
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 = (p.y() >= 0 && rect().contains(p)) ? (p.y() / rh) : -1;
if (newFilteredSel != _filteredSel) {
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 AddParticipantInner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
_time = unixtime();
@ -324,22 +503,32 @@ void AddParticipantInner::updateFilter(QString filter) {
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) {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
resize(width(), _contacts->list.count * rh);
_sel = _contacts->list.begin;
while (_sel->next->next &&& contactData(_sel)->inchat) {
_sel = _sel->next;
}
} else {
resize(width(), st::noContactsHeight);
if (cContactsReceived()) {
if (_addContactLnk.isHidden()) _addContactLnk.show();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
}
}
if (!_sel && !_byUsername.isEmpty()) {
_byUsernameSel = 0;
while (_byUsernameSel < _byUsername.size() && d_byUsername[_byUsernameSel]->inchat) {
++_byUsernameSel;
}
if (_byUsernameSel == _byUsername.size()) _byUsernameSel = -1;
} else {
_byUsernameSel = -1;
}
refresh();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
@ -381,17 +570,48 @@ void AddParticipantInner::updateFilter(QString filter) {
}
}
}
_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;
while (_filteredSel < _filtered.size() - 1 && contactData(_filtered[_filteredSel])->inchat) {
++_filteredSel;
_filteredSel = -1;
if (!_filtered.isEmpty()) {
for (_filteredSel = 0; (_filteredSel < _filtered.size()) && contactData(_filtered[_filteredSel])->inchat;) {
++_filteredSel;
}
if (_filteredSel == _filtered.size()) _filteredSel = -1;
}
_byUsernameSel = -1;
if (_filteredSel < 0 && !_byUsernameFiltered.isEmpty()) {
for (_byUsernameSel = 0; (_byUsernameSel < _byUsernameFiltered.size()) && d_byUsernameFiltered[_byUsernameSel]->inchat;) {
++_byUsernameSel;
}
if (_byUsernameSel == _byUsernameFiltered.size()) _byUsernameSel = -1;
}
if (!_filtered.isEmpty()) {
resize(width(), _filtered.size() * rh);
} else {
resize(width(), st::noContactsHeight);
}
refresh();
_searching = true;
emit searchByUsername();
}
if (parentWidget()) parentWidget()->update();
loadProfilePhotos(0);
@ -427,7 +647,13 @@ void AddParticipantInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newR
}
AddParticipantInner::~AddParticipantInner() {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
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;
}
}
@ -441,82 +667,106 @@ void AddParticipantInner::selectSkip(int32 dir) {
_mouseSel = false;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, origDir = dir;
if (_filter.isEmpty()) {
int cur = 0;
if (_sel) {
if (dir > 0) {
while (dir && _sel->next->next) {
_sel = _sel->next;
--dir;
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 (dir > 0) {
while (_sel && _sel->next && contactData(_sel)->inchat) {
_sel = _sel->next;
}
if (!_sel || !_sel->next) {
_sel = 0;
if (!_byUsername.isEmpty()) {
if (_byUsernameSel < 0) _byUsernameSel = 0;
for (; _byUsernameSel < _byUsername.size() && d_byUsername[_byUsernameSel]->inchat;) {
++_byUsernameSel;
}
if (_byUsernameSel == _byUsername.size()) _byUsernameSel = -1;
}
while (contactData(_sel)->inchat && _sel->next->next) {
_sel = _sel->next;
}
if (contactData(_sel)->inchat) {
while (contactData(_sel)->inchat && _sel->prev) {
}
} else {
while (_byUsernameSel >= 0 && d_byUsername[_byUsernameSel]->inchat) {
--_byUsernameSel;
}
if (_byUsernameSel < 0) {
if (_contacts->list.count) {
if (!_sel) _sel = _contacts->list.end->prev;
for (; _sel && contactData(_sel)->inchat;) {
_sel = _sel->prev;
}
}
} else {
while (dir && _sel->prev) {
_sel = _sel->prev;
++dir;
}
while (contactData(_sel)->inchat && _sel->prev) {
_sel = _sel->prev;
}
if (contactData(_sel)->inchat) {
while (contactData(_sel)->inchat && _sel->next->next) {
_sel = _sel->next;
}
}
}
} else if (dir > 0 && _contacts->list.count) {
_sel = _contacts->list.begin;
while (contactData(_sel)->inchat && _sel->next->next) {
_sel = _sel->next;
}
}
if (_sel) {
if (contactData(_sel)->inchat) {
_sel = 0;
} else {
emit mustScrollTo(_sel->pos * rh, (_sel->pos + 1) * rh);
}
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 (dir > 0) {
if (_filteredSel < 0 && dir > 1) {
_filteredSel = 0;
}
_filteredSel += dir;
while (_filteredSel < _filtered.size() - 1 && contactData(_filtered[_filteredSel])->inchat) {
while (_filteredSel >= 0 && _filteredSel < _filtered.size() && contactData(_filtered[_filteredSel])->inchat) {
++_filteredSel;
}
if (_filteredSel >= _filtered.size()) {
_filteredSel = _filtered.size() - 1;
}
while (_filteredSel > 0 && contactData(_filtered[_filteredSel])->inchat) {
--_filteredSel;
}
} else if (_filteredSel > 0) {
_filteredSel += dir;
if (_filteredSel < 0) {
_filteredSel = 0;
}
if (_filteredSel < _filtered.size() - 1) {
while (_filteredSel > 0 && contactData(_filtered[_filteredSel])->inchat) {
--_filteredSel;
if (_filteredSel < 0 || _filteredSel >= _filtered.size()) {
_filteredSel = -1;
if (!_byUsernameFiltered.isEmpty()) {
if (_byUsernameSel < 0) _byUsernameSel = 0;
for (; _byUsernameSel < _byUsernameFiltered.size() && d_byUsernameFiltered[_byUsernameSel]->inchat;) {
++_byUsernameSel;
}
if (_byUsernameSel == _byUsernameFiltered.size()) _byUsernameSel = -1;
}
}
while (_filteredSel < _filtered.size() - 1 && contactData(_filtered[_filteredSel])->inchat) {
++_filteredSel;
} else {
while (_byUsernameSel >= 0 && d_byUsernameFiltered[_byUsernameSel]->inchat) {
--_byUsernameSel;
}
if (_byUsernameSel < 0) {
if (!_filtered.isEmpty()) {
if (_filteredSel < 0) _filteredSel = _filtered.size() - 1;
for (; _filteredSel >= 0 && contactData(_filtered[_filteredSel])->inchat;) {
--_filteredSel;
}
}
}
}
if (_filteredSel >= 0) {
if (contactData(_filtered[_filteredSel])->inchat) {
_filteredSel = -1;
} else {
emit mustScrollTo(_filteredSel * rh, (_filteredSel + 1) * rh);
}
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();
@ -534,7 +784,7 @@ AddParticipantBox::AddParticipantBox(ChatData *chat) :
_filter(this, st::contactsFilter, lang(lng_participant_filter)),
_invite(this, lang(lng_participant_invite), st::btnSelectDone),
_cancel(this, lang(lng_cancel), st::btnSelectCancel),
_hiding(false), a_opacity(0, 1), af_opacity(anim::linear) {
_hiding(false), a_opacity(0, 1) {
_width = st::participantWidth;
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
@ -553,12 +803,81 @@ AddParticipantBox::AddParticipantBox(ChatData *chat) :
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 AddParticipantBox::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(&AddParticipantBox::peopleReceived), rpcFail(&AddParticipantBox::peopleFailed));
_peopleQueries.insert(_peopleRequest, _peopleQuery);
}
}
return false;
}
void AddParticipantBox::onNeedSearchByUsername() {
if (!onSearchByUsername(true)) {
_searchTimer.start(AutoSearchTimeout);
}
}
void AddParticipantBox::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 AddParticipantBox::peopleFailed(const RPCError &error, mtpRequestId req) {
if (_peopleRequest == req) {
_peopleRequest = 0;
_peopleFull = true;
}
return true;
}
void AddParticipantBox::hideAll() {
_filter.hide();
_scroll.hide();
@ -643,7 +962,7 @@ void AddParticipantBox::animStep(float64 dt) {
_filter.setFocus();
}
} else {
a_opacity.update(dt, af_opacity);
a_opacity.update(dt, anim::linear);
}
update();
}

View file

@ -22,6 +22,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
class AddParticipantInner : public QWidget, public RPCSender {
Q_OBJECT
private:
struct ContactData;
public:
AddParticipantInner(ChatData *chat);
@ -33,7 +37,7 @@ public:
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void paintDialog(QPainter &p, DialogRow *row, bool sel);
void paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel);
void updateFilter(QString filter = QString());
void selectSkip(int32 dir);
@ -42,6 +46,11 @@ public:
void loadProfilePhotos(int32 yFrom);
void chooseParticipant();
void changeCheckState(DialogRow *row);
void changeCheckState(ContactData *data);
void peopleReceived(const QString &query, const QVector<MTPContactFound> &people);
void refresh();
ChatData *chat();
QVector<UserData*> selected();
@ -52,6 +61,7 @@ signals:
void mustScrollTo(int ymin, int ymax);
void selectAllQuery();
void searchByUsername();
public slots:
@ -76,23 +86,32 @@ private:
int32 _selCount;
typedef struct {
struct ContactData {
Text name;
QString online;
bool inchat;
bool check;
} ContactData;
};
typedef QMap<UserData*, ContactData*> ContactsData;
ContactsData _contactsData;
ContactData *contactData(DialogRow *row);
bool _searching;
QString _lastQuery;
typedef QVector<UserData*> ByUsernameRows;
typedef QVector<ContactData*> ByUsernameDatas;
ByUsernameRows _byUsername, _byUsernameFiltered;
ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas
ByUsernameDatas _byUsernameDatas;
int32 _byUsernameSel;
QPoint _lastMousePos;
LinkButton _addContactLnk;
};
class AddParticipantBox : public LayeredWidget {
class AddParticipantBox : public LayeredWidget, public RPCSender {
Q_OBJECT
public:
@ -113,6 +132,9 @@ public slots:
void onScroll();
void onInvite();
bool onSearchByUsername(bool searchCache = false);
void onNeedSearchByUsername();
private:
void hideAll();
@ -129,5 +151,18 @@ private:
QPixmap _cache;
anim::fvalue a_opacity;
anim::transition af_opacity;
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req);
bool peopleFailed(const RPCError &error, mtpRequestId req);
QTimer _searchTimer;
QString _peopleQuery;
bool _peopleFull;
mtpRequestId _peopleRequest;
typedef QMap<QString, MTPcontacts_Found> PeopleCache;
PeopleCache _peopleCache;
typedef QMap<mtpRequestId, QString> PeopleQueries;
PeopleQueries _peopleQueries;
};

View file

@ -22,12 +22,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#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()));
@ -88,6 +92,16 @@ void NewGroupInner::loadProfilePhotos(int32 yFrom) {
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;
@ -99,6 +113,16 @@ void NewGroupInner::loadProfilePhotos(int32 yFrom) {
_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();
}
}
}
}
@ -120,12 +144,9 @@ NewGroupInner::ContactData *NewGroupInner::contactData(DialogRow *row) {
return data;
}
void NewGroupInner::paintDialog(QPainter &p, DialogRow *row, bool sel) {
void NewGroupInner::paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel) {
int32 left = st::profileListPadding.width();
UserData *user = row->history->peer->asUser();
ContactData *data = contactData(row);
if (_selCount >= cMaxGroupCount() && !data->check) {
sel = false;
}
@ -147,13 +168,29 @@ void NewGroupInner::paintDialog(QPainter &p, DialogRow *row, bool sel) {
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 (data->check) {
p.setPen(st::white->p);
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 {
p.setPen((App::onlineColorUse(user->onlineTill, _time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
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);
}
p.drawText(left + st::profileListPhotoSize + st::participantDelta, st::profileListPadding.height() + st::profileListPhotoSize - 6, data->online);
}
void NewGroupInner::paintEvent(QPaintEvent *e) {
@ -163,40 +200,86 @@ void NewGroupInner::paintEvent(QPaintEvent *e) {
_time = unixtime();
p.fillRect(r, st::white->b);
int32 yFrom = r.top();
int32 yFrom = r.top(), yTo = r.bottom();
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
_contacts->list.adjustCurrent(yFrom, rh);
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, (drawFrom == _sel));
p.translate(0, rh);
drawFrom = drawFrom->next;
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() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
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()) {
if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_not_found), style::al_center);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang((cContactsReceived() && !_searching) ? lng_no_contacts : lng_contacts_not_found), style::al_center);
} else {
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();
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], (_filteredSel == from));
p.translate(0, rh);
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);
}
}
}
}
@ -209,7 +292,11 @@ void NewGroupInner::enterEvent(QEvent *e) {
void NewGroupInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
updateSel();
if (_sel || _filteredSel >= 0 || _byUsernameSel >= 0) {
_sel = 0;
_filteredSel = _byUsernameSel = -1;
parentWidget()->update();
}
}
void NewGroupInner::mouseMoveEvent(QMouseEvent *e) {
@ -228,11 +315,15 @@ void NewGroupInner::mousePressEvent(QMouseEvent *e) {
}
void NewGroupInner::changeCheckState(DialogRow *row) {
if (contactData(row)->check) {
contactData(row)->check = false;
changeCheckState(contactData(row));
}
void NewGroupInner::changeCheckState(ContactData *data) {
if (data->check) {
data->check = false;
--_selCount;
} else if (_selCount < cMaxGroupCount()) {
contactData(row)->check = true;
data->check = true;
++_selCount;
}
}
@ -240,42 +331,125 @@ void NewGroupInner::changeCheckState(DialogRow *row) {
void NewGroupInner::chooseParticipant() {
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
if (_filter.isEmpty()) {
if (!_sel) return;
changeCheckState(_sel);
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) {
changeCheckState(d_byUsername[_byUsernameSel]);
} else {
if (!_sel) return;
changeCheckState(_sel);
}
} else {
if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return;
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) {
changeCheckState(d_byUsernameFiltered[_byUsernameSel]);
DialogRow *row = _filtered[_filteredSel];
changeCheckState(row);
PeerData *peer = row->history->peer;
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<MTPContactFound> &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<MTPContactFound>::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;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
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 = rect().contains(p) ? _contacts->list.rowAtY(p.y(), rh) : 0;
if (newSel != _sel) {
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 = (p.y() >= 0 && rect().contains(p)) ? (p.y() / rh) : -1;
if (newFilteredSel != _filteredSel) {
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;
@ -294,20 +468,23 @@ void NewGroupInner::updateFilter(QString filter) {
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()) {
resize(width(), _contacts->list.count * rh);
_sel = 0;
if (_contacts->list.count) {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
resize(width(), _contacts->list.count * rh);
_sel = _contacts->list.begin;
} else {
resize(width(), st::noContactsHeight);
if (cContactsReceived()) {
if (_addContactLnk.isHidden()) _addContactLnk.show();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
}
}
if (!_sel && !_byUsername.isEmpty()) {
_byUsernameSel = 0;
}
refresh();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
@ -349,14 +526,36 @@ void NewGroupInner::updateFilter(QString filter) {
}
}
}
_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;
if (!_filtered.isEmpty()) {
resize(width(), _filtered.size() * rh);
} else {
resize(width(), st::noContactsHeight);
}
refresh();
_searching = true;
emit searchByUsername();
}
if (parentWidget()) parentWidget()->update();
loadProfilePhotos(0);
@ -392,7 +591,13 @@ void NewGroupInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
}
NewGroupInner::~NewGroupInner() {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
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;
}
}
@ -405,41 +610,52 @@ 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) {
if (dir > 0) {
while (dir && _sel->next->next) {
_sel = _sel->next;
--dir;
}
} else {
while (dir && _sel->prev) {
_sel = _sel->prev;
++dir;
}
for (DialogRow *i = _contacts->list.begin; i != _sel; i = i->next) {
++cur;
}
} else if (dir > 0 && _contacts->list.count) {
_sel = _contacts->list.begin;
} 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 {
if (dir > 0) {
if (_filteredSel < 0 && dir > 1) {
_filteredSel = 0;
}
_filteredSel += dir;
if (_filteredSel >= _filtered.size()) {
_filteredSel = _filtered.size() - 1;
}
} else if (_filteredSel > 0) {
_filteredSel += dir;
if (_filteredSel < 0) {
_filteredSel = 0;
}
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();
@ -460,6 +676,11 @@ QVector<MTPInputUser> NewGroupInner::selectedInputs() {
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;
}
@ -469,6 +690,11 @@ PeerData *NewGroupInner::selectedUser() {
return i.key();
}
}
for (int32 i = 0, l = _byUsername.size(); i < l; ++i) {
if (d_byUsername[i]->check) {
return _byUsername[i];
}
}
return 0;
}
@ -495,12 +721,81 @@ NewGroupBox::NewGroupBox() : _scroll(this, st::newGroupScroll), _inner(),
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();

View file

@ -22,6 +22,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
class NewGroupInner : public QWidget {
Q_OBJECT
private:
struct ContactData;
public:
NewGroupInner();
@ -33,7 +37,7 @@ public:
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void paintDialog(QPainter &p, DialogRow *row, bool sel);
void paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel);
void updateFilter(QString filter = QString());
void selectSkip(int32 dir);
@ -45,6 +49,11 @@ public:
void loadProfilePhotos(int32 yFrom);
void changeCheckState(DialogRow *row);
void changeCheckState(ContactData *data);
void peopleReceived(const QString &query, const QVector<MTPContactFound> &people);
void refresh();
~NewGroupInner();
@ -52,6 +61,7 @@ signals:
void mustScrollTo(int ymin, int ymax);
void selectAllQuery();
void searchByUsername();
public slots:
@ -74,17 +84,26 @@ private:
int32 _filteredSel;
bool _mouseSel;
typedef struct {
struct ContactData {
Text name;
QString online;
bool check;
} ContactData;
};
typedef QMap<UserData*, ContactData*> ContactsData;
ContactsData _contactsData;
int32 _selCount;
ContactData *contactData(DialogRow *row);
bool _searching;
QString _lastQuery;
typedef QVector<UserData*> ByUsernameRows;
typedef QVector<ContactData*> ByUsernameDatas;
ByUsernameRows _byUsername, _byUsernameFiltered;
ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas
ByUsernameDatas _byUsernameDatas;
int32 _byUsernameSel;
QPoint _lastMousePos;
LinkButton _addContactLnk;
@ -111,6 +130,9 @@ public slots:
void onNext();
void onScroll();
bool onSearchByUsername(bool searchCache = false);
void onNeedSearchByUsername();
private:
void hideAll();
@ -126,6 +148,20 @@ private:
QPixmap _cache;
anim::fvalue a_opacity;
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req);
bool peopleFailed(const RPCError &error, mtpRequestId req);
QTimer _searchTimer;
QString _peopleQuery;
bool _peopleFull;
mtpRequestId _peopleRequest;
typedef QMap<QString, MTPcontacts_Found> PeopleCache;
PeopleCache _peopleCache;
typedef QMap<mtpRequestId, QString> PeopleQueries;
PeopleQueries _peopleQueries;
};
class CreateGroupBox : public LayeredWidget, public RPCSender {

View file

@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 7024;
static const wchar_t *AppVersionStr = L"0.7.24";
static const int32 AppVersion = 7025;
static const wchar_t *AppVersionStr = L"0.7.25";
static const bool DevChannel = true;
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";

View file

@ -900,26 +900,40 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (_rows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
}
p.setPen(st::black->p);
if (_rows->isEmpty()) {
QString tag = st::mentionFont->m.elidedText('#' + _hrows->at(last - i - 1), Qt::ElideRight, htagwidth);
p.setFont(st::mentionFont->f);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, tag);
} else {
UserData *user = _rows->at(last - i - 1);
QString uname = user->username;
int32 unamewidth = atwidth + st::mentionFont->m.width(uname), namewidth = user->nameText.maxWidth();
QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (availwidth < unamewidth + namewidth) {
namewidth = (availwidth * namewidth) / (namewidth + unamewidth);
unamewidth = availwidth - namewidth;
uname = st::mentionFont->m.elidedText('@' + uname, Qt::ElideRight, unamewidth);
} else {
uname = '@' + uname;
if (firstwidth <= unamewidth) {
if (firstwidth < unamewidth) {
first = st::mentionFont->m.elidedText(first, Qt::ElideRight, unamewidth);
} else if (!second.isEmpty()) {
first = st::mentionFont->m.elidedText(first + second, Qt::ElideRight, unamewidth);
second = QString();
}
} else {
second = st::mentionFont->m.elidedText(second, Qt::ElideRight, unamewidth - firstwidth);
}
}
user->photo->load();
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pix(st::mentionPhotoSize));
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
p.setFont(st::mentionFont->f);
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, uname);
p.setPen(st::profileOnlineColor->p);
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
if (!second.isEmpty()) {
p.setPen(st::profileOfflineColor->p);
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
}
}
@ -1240,6 +1254,10 @@ bool MentionsDropdown::animStep(float64 ms) {
return res;
}
const QString &MentionsDropdown::filter() const {
return _filter;
}
int32 MentionsDropdown::innerTop() {
return _scroll.scrollTop();
}
@ -1257,7 +1275,7 @@ bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
return true;
} else if (ev->key() == Qt::Key_Down) {
return _inner.moveSel(1);
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Space) {
return _inner.select();
}
}

View file

@ -291,6 +291,8 @@ public:
bool animStep(float64 ms);
const QString &filter() const;
int32 innerTop();
int32 innerBottom();

View file

@ -718,6 +718,8 @@ void MainWidget::addParticipantDone(ChatData *chat, const MTPmessages_StatedMess
}
bool MainWidget::addParticipantFail(ChatData *chat, const RPCError &e) {
ConfirmBox *box = new ConfirmBox(lang(lng_failed_add_participant), true);
App::wnd()->showLayer(box);
if (e.type() == "USER_LEFT_CHAT") { // trying to return banned user to his group
}
return false;

View file

@ -200,6 +200,7 @@ namespace {
}
} else {
secs = m.captured(1).toInt();
if (secs >= 60) return false;
}
uint64 sendAt = getms(true) + secs * 1000 + 10;
DelayedRequestsList::iterator i = delayedRequests.begin(), e = delayedRequests.end();

View file

@ -717,6 +717,9 @@ void ProfileInner::contextMenuEvent(QContextMenuEvent *e) {
if (info.contains(mapFromGlobal(e->globalPos()))) {
_menu = new ContextMenu(this);
_menu->addAction(lang(lng_profile_copy_phone), this, SLOT(onCopyPhone()))->setEnabled(true);
if (_peerUser && !_peerUser->username.isEmpty()) {
_menu->addAction(lang(lng_context_copy_mention), this, SLOT(onCopyUsername()))->setEnabled(true);
}
_menu->deleteOnHide();
connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
_menu->popup(e->globalPos());
@ -735,6 +738,10 @@ void ProfileInner::onCopyPhone() {
QApplication::clipboard()->setText(_phoneText);
}
void ProfileInner::onCopyUsername() {
QApplication::clipboard()->setText('@' + _peerUser->username);
}
bool ProfileInner::animStep(float64 ms) {
float64 dt = ms / st::setPhotoDuration;
bool res = true;

View file

@ -91,6 +91,7 @@ public slots:
void onMenuDestroy(QObject *obj);
void onCopyPhone();
void onCopyUsername();
private:

View file

@ -11,7 +11,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.7.24</string>
<string>0.7.25</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

Binary file not shown.

View file

@ -1667,7 +1667,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.24;
CURRENT_PROJECT_VERSION = 0.7.25;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@ -1685,7 +1685,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 0.7.24;
CURRENT_PROJECT_VERSION = 0.7.25;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_OPTIMIZATION_LEVEL = fast;
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@ -1711,10 +1711,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.24;
CURRENT_PROJECT_VERSION = 0.7.25;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 0.7;
DYLIB_CURRENT_VERSION = 0.7.24;
DYLIB_CURRENT_VERSION = 0.7.25;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@ -1852,10 +1852,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.24;
CURRENT_PROJECT_VERSION = 0.7.25;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.7;
DYLIB_CURRENT_VERSION = 0.7.24;
DYLIB_CURRENT_VERSION = 0.7.25;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;

View file

@ -1,2 +1,2 @@
echo 7024 0.7.24 1
echo 7025 0.7.25 1
# AppVersion AppVersionStr DevChannel