tdesktop/Telegram/SourceFiles/profilewidget.cpp

1101 lines
36 KiB
C++
Raw Normal View History

/*
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 "window.h"
#include "mainwidget.h"
#include "profilewidget.h"
#include "boxes/addcontactbox.h"
#include "boxes/confirmbox.h"
#include "boxes/photocropbox.h"
#include "application.h"
#include "boxes/contactsbox.h"
#include "gui/filedialog.h"
ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const PeerData *peer) : TWidget(0),
_profile(profile), _scroll(scroll), _peer(App::peer(peer->id)),
2014-08-15 11:19:32 +00:00
_peerUser(_peer->chat ? 0 : _peer->asUser()), _peerChat(_peer->chat ? _peer->asChat() : 0), _hist(App::history(peer->id)),
_chatAdmin(_peerChat ? (_peerChat->admin == MTP::authedId()) : false),
// profile
_nameCache(peer->name),
_uploadPhoto(this, lang(lng_profile_set_group_photo), st::btnShareContact),
_addParticipant(this, lang(lng_profile_add_participant), st::btnShareContact),
_sendMessage(this, lang(lng_profile_send_message), st::btnShareContact),
_shareContact(this, lang(lng_profile_share_contact), st::btnShareContact),
_cancelPhoto(this, lang(lng_cancel)),
_createInvitationLink(this, lang(lng_group_invite_create)),
_invitationLink(this, qsl("telegram.me/joinchat/")),
a_photo(0),
_photoOver(false),
// settings
_enableNotifications(this, lang(lng_profile_enable_notifications)),
_clearHistory(this, lang(lng_profile_clear_history)),
2014-08-15 11:19:32 +00:00
// shared media
_allMediaTypes(false),
_mediaShowAll(this, lang(lng_profile_show_all_types)),
_mediaPhotos(this, QString()),
_mediaVideos(this, QString()),
_mediaDocuments(this, QString()),
_mediaAudios(this, QString()),
// participants
_pHeight(st::profileListPhotoSize + st::profileListPadding.height() * 2),
_kickWidth(st::linkFont->m.width(lang(lng_profile_kick))),
_selectedRow(-1), _lastPreload(0), _contactId(0),
_kickOver(0), _kickDown(0), _kickConfirm(0),
_menu(0) {
connect(App::api(), SIGNAL(fullPeerLoaded(PeerData*)), this, SLOT(onFullPeerLoaded(PeerData*)));
if (_peerUser) {
_phoneText = _peerUser->phone.isEmpty() ? QString() : App::formatPhone(_peerUser->phone);
App::api()->requestFullPeer(_peer);
} else if (_peerChat->photoId) {
PhotoData *ph = App::photo(_peerChat->photoId);
if (ph->date) {
_photoLink = TextLinkPtr(new PhotoLink(ph, _peer));
}
} else {
App::api()->requestFullPeer(_peer);
}
// profile
_nameText.setText(st::profileNameFont, _nameCache, _textNameOptions);
connect(&_uploadPhoto, SIGNAL(clicked()), this, SLOT(onUpdatePhoto()));
connect(&_addParticipant, SIGNAL(clicked()), this, SLOT(onAddParticipant()));
connect(&_sendMessage, SIGNAL(clicked()), this, SLOT(onSendMessage()));
connect(&_shareContact, SIGNAL(clicked()), this, SLOT(onShareContact()));
connect(&_cancelPhoto, SIGNAL(clicked()), this, SLOT(onUpdatePhotoCancel()));
connect(&_createInvitationLink, SIGNAL(clicked()), this, SLOT(onCreateInvitationLink()));
connect(&_invitationLink, SIGNAL(clicked()), this, SLOT(onInvitationLink()));
_invitationLink.setAcceptBoth(true);
updateInvitationLink();
connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId)));
connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
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 *)));
// settings
connect(&_enableNotifications, SIGNAL(clicked()), this, SLOT(onEnableNotifications()));
connect(&_clearHistory, SIGNAL(clicked()), this, SLOT(onClearHistory()));
2014-08-15 11:19:32 +00:00
// shared media
connect(&_mediaShowAll, SIGNAL(clicked()), this, SLOT(onMediaShowAll()));
connect(&_mediaPhotos, SIGNAL(clicked()), this, SLOT(onMediaPhotos()));
connect(&_mediaVideos, SIGNAL(clicked()), this, SLOT(onMediaVideos()));
connect(&_mediaDocuments, SIGNAL(clicked()), this, SLOT(onMediaDocuments()));
connect(&_mediaAudios, SIGNAL(clicked()), this, SLOT(onMediaAudios()));
_mediaLinks[OverviewPhotos] = &_mediaPhotos;
_mediaLinks[OverviewVideos] = &_mediaVideos;
_mediaLinks[OverviewDocuments] = &_mediaDocuments;
_mediaLinks[OverviewAudios] = &_mediaAudios;
App::main()->preloadOverviews(_peer);
App::contextItem(0);
resizeEvent(0);
showAll();
}
void ProfileInner::onShareContact() {
App::main()->shareContactLayer(_peerUser);
}
void ProfileInner::onSendMessage() {
App::main()->showPeer(_peer->id);
}
void ProfileInner::onEnableNotifications() {
App::main()->updateNotifySetting(_peer, _enableNotifications.checked());
}
void ProfileInner::saveError(const QString &str) {
_errorText = str;
resizeEvent(0);
showAll();
update();
}
void ProfileInner::loadProfilePhotos(int32 yFrom) {
_lastPreload = yFrom;
int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5;
MTP::clearLoaderPriorities();
2014-08-15 11:19:32 +00:00
int32 partfrom = _mediaAudios.y() + _mediaAudios.height() + st::profileHeaderSkip;
yFrom -= partfrom;
yTo -= partfrom;
if (yTo < 0) return;
if (yFrom < 0) yFrom = 0;
yFrom /= _pHeight;
yTo = yTo / _pHeight + 1;
if (yFrom >= _participants.size()) return;
if (yTo > _participants.size()) yTo = _participants.size();
for (int32 i = yFrom; i < yTo; ++i) {
_participants[i]->photo->load();
}
}
void ProfileInner::onUpdatePhoto() {
saveError();
QStringList imgExtensions(cImgExtensions());
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;All files (*.*)"));
QImage img;
QString file;
QByteArray remoteContent;
if (filedialogGetOpenFile(file, remoteContent, lang(lng_choose_images), filter)) {
if (!remoteContent.isEmpty()) {
img = App::readImage(remoteContent);
} else {
if (!file.isEmpty()) {
img = App::readImage(file);
}
}
} else {
return;
}
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
saveError(lang(lng_bad_photo));
return;
}
PhotoCropBox *box = new PhotoCropBox(img, _peer->id);
connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart()));
App::wnd()->showLayer(box);
}
void ProfileInner::onClearHistory() {
ConfirmBox *box = new ConfirmBox(lng_sure_delete_history(lt_contact, _peer->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onClearHistorySure()));
App::wnd()->showLayer(box);
}
void ProfileInner::onClearHistorySure() {
App::main()->showPeer(0, 0, true);
App::wnd()->hideLayer();
App::main()->clearHistory(_peer);
}
void ProfileInner::onAddParticipant() {
App::wnd()->showLayer(new ContactsBox(_peerChat));
}
void ProfileInner::onUpdatePhotoCancel() {
App::app()->cancelPhotoUpdate(_peer->id);
showAll();
update();
}
void ProfileInner::onPhotoUpdateStart() {
showAll();
update();
}
void ProfileInner::onPhotoUpdateFail(PeerId peer) {
if (_peer->id != peer) return;
saveError(lang(lng_bad_photo));
showAll();
update();
}
void ProfileInner::onPhotoUpdateDone(PeerId peer) {
if (_peer->id != peer) return;
saveError();
showAll();
update();
}
2014-08-15 11:19:32 +00:00
void ProfileInner::onMediaShowAll() {
_allMediaTypes = true;
resizeEvent(0);
showAll();
}
void ProfileInner::onMediaPhotos() {
App::main()->showMediaOverview(_peer, OverviewPhotos);
}
void ProfileInner::onMediaVideos() {
App::main()->showMediaOverview(_peer, OverviewVideos);
}
void ProfileInner::onMediaDocuments() {
App::main()->showMediaOverview(_peer, OverviewDocuments);
}
void ProfileInner::onMediaAudios() {
App::main()->showMediaOverview(_peer, OverviewAudios);
}
void ProfileInner::onInvitationLink() {
2015-05-19 15:46:45 +00:00
DEBUG_LOG(("Setting text to clipboard from invite url: %1").arg(_peerChat->invitationUrl));
QApplication::clipboard()->setText(_peerChat->invitationUrl);
App::wnd()->showLayer(new ConfirmBox(lang(lng_group_invite_copied), true));
}
void ProfileInner::onCreateInvitationLink() {
ConfirmBox *box = new ConfirmBox(lang(_peerChat->invitationUrl.isEmpty() ? lng_group_invite_about : lng_group_invite_about_new));
connect(box, SIGNAL(confirmed()), this, SLOT(onCreateInvitationLinkSure()));
App::wnd()->showLayer(box);
}
void ProfileInner::onCreateInvitationLinkSure() {
MTP::send(MTPmessages_ExportChatInvite(App::peerToMTP(_peerChat->id).c_peerChat().vchat_id), rpcDone(&ProfileInner::chatInviteDone));
}
void ProfileInner::chatInviteDone(const MTPExportedChatInvite &result) {
_peerChat->invitationUrl = (result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString();
updateInvitationLink();
showAll();
resizeEvent(0);
App::wnd()->hideLayer();
}
void ProfileInner::onFullPeerLoaded(PeerData *peer) {
if (peer != _peer) return;
if (_peerUser) {
PhotoData *userPhoto = _peerUser->photoId ? App::photo(_peerUser->photoId) : 0;
if (userPhoto && userPhoto->date) {
_photoLink = TextLinkPtr(new PhotoLink(userPhoto, _peer));
} else {
_photoLink = TextLinkPtr();
}
} else if (_peerChat) {
updateInvitationLink();
showAll();
resizeEvent(0);
}
}
void ProfileInner::peerUpdated(PeerData *data) {
if (data == _peer) {
PhotoData *photo = 0;
if (_peerUser) {
_phoneText = _peerUser->phone.isEmpty() ? QString() : App::formatPhone(_peerUser->phone);
if (_peerUser->photoId) photo = App::photo(_peerUser->photoId);
} else {
if (_peerChat->photoId) photo = App::photo(_peerChat->photoId);
}
_photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr();
if (_peer->name != _nameCache) {
_nameCache = _peer->name;
_nameText.setText(st::profileNameFont, _nameCache, _textNameOptions);
}
}
showAll();
update();
}
void ProfileInner::updateOnlineDisplay() {
reorderParticipants();
update();
}
void ProfileInner::updateOnlineDisplayTimer() {
int32 t = unixtime(), minIn = 86400;
if (_peerChat) {
if (_peerChat->participants.isEmpty()) return;
for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) {
int32 onlineWillChangeIn = App::onlineWillChangeIn(i.key(), t);
if (onlineWillChangeIn < minIn) {
minIn = onlineWillChangeIn;
}
}
} else {
minIn = App::onlineWillChangeIn(_peerUser, t);
}
App::main()->updateOnlineDisplayIn(minIn * 1000);
}
void ProfileInner::reorderParticipants() {
int32 was = _participants.size(), t = unixtime(), onlineCount = 0;
if (_peerChat && !_peerChat->forbidden) {
if (_peerChat->count <= 0 || !_peerChat->participants.isEmpty()) {
_participants.clear();
for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) {
if (*i) {
delete *i;
*i = 0;
}
}
_participants.reserve(_peerChat->participants.size());
_participantsData.resize(_peerChat->participants.size());
}
UserData *self = App::self();
bool onlyMe = true;
for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
int32 until = App::onlineForSort(user, t);
Participants::iterator before = _participants.begin();
if (user != self) {
if (before != _participants.end() && (*before) == self) {
++before;
}
while (before != _participants.end() && App::onlineForSort(*before, t) >= until) {
++before;
}
if (until > t && onlyMe) onlyMe = false;
}
_participants.insert(before, user);
if (until > t) {
++onlineCount;
}
}
if (_peerChat->count > 0 && _participants.isEmpty()) {
App::api()->requestFullPeer(_peer);
if (_onlineText.isEmpty()) _onlineText = lng_chat_status_members(lt_count, _peerChat->count);
} else if (onlineCount && !onlyMe) {
_onlineText = lng_chat_status_members_online(lt_count, _participants.size(), lt_count_online, onlineCount);
} else {
_onlineText = lng_chat_status_members(lt_count, _participants.size());
}
loadProfilePhotos(_lastPreload);
} else {
_participants.clear();
if (_peerUser) {
_onlineText = App::onlineText(_peerUser, t, true);
} else {
_onlineText = lang(lng_chat_status_unaccessible);
}
}
if (was != _participants.size()) {
resizeEvent(0);
}
}
bool ProfileInner::event(QEvent *e) {
if (e->type() == QEvent::MouseMove) {
_lastPos = static_cast<QMouseEvent*>(e)->globalPos();
updateSelected();
}
return QWidget::event(e);
}
void ProfileInner::paintEvent(QPaintEvent *e) {
QPainter p(this);
QRect r(e->rect());
p.setClipRect(r);
int32 top = 0, l_time = unixtime();
// profile
top += st::profilePadding.top();
if (_photoLink || !_peerChat || _peerChat->forbidden) {
p.drawPixmap(_left, top, _peer->photo->pix(st::profilePhotoSize));
} else {
if (a_photo.current() < 1) {
p.drawPixmap(QPoint(_left, top), App::sprite(), st::setPhotoImg);
}
if (a_photo.current() > 0) {
p.setOpacity(a_photo.current());
p.drawPixmap(QPoint(_left, top), App::sprite(), st::setOverPhotoImg);
p.setOpacity(1);
}
}
p.setPen(st::black->p);
_nameText.drawElided(p, _left + st::profilePhotoSize + st::profileNameLeft, top + st::profileNameTop, _width - st::profilePhotoSize - st::profileNameLeft);
p.setFont(st::profileStatusFont->f);
2014-10-22 18:39:03 +00:00
int32 addbyname = 0;
if (_peerUser && !_peerUser->username.isEmpty()) {
addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent);
p.setPen(st::black->p);
p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop + st::linkFont->ascent, '@' + _peerUser->username);
}
p.setPen((_peerUser && App::onlineColorUse(_peerUser->onlineTill, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
2014-10-22 18:39:03 +00:00
p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText);
if (_chatAdmin && !_peerChat->invitationUrl.isEmpty()) {
p.setPen(st::black->p);
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _createInvitationLink.y() + st::linkFont->ascent, lang(lng_group_invite_link));
}
if (!_cancelPhoto.isHidden()) {
p.setPen(st::profileOfflineColor->p);
2014-10-22 18:39:03 +00:00
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + addbyname + st::linkFont->ascent, lang(lng_settings_uploading_photo));
}
if (!_errorText.isEmpty()) {
p.setFont(st::setErrFont->f);
p.setPen(st::setErrColor->p);
2014-10-22 18:39:03 +00:00
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + addbyname + st::profilePhoneTop + st::profilePhoneFont->ascent, _errorText);
}
if (!_phoneText.isEmpty()) {
p.setPen(st::black->p);
p.setFont(st::linkFont->f);
2014-10-22 18:39:03 +00:00
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + addbyname + st::profilePhoneTop + st::profilePhoneFont->ascent, _phoneText);
}
top += st::profilePhotoSize;
top += st::profileButtonTop;
if (_peerChat && _peerChat->forbidden) {
int32 w = st::btnShareContact.font->m.width(lang(lng_profile_chat_unaccessible));
p.setFont(st::btnShareContact.font->f);
p.setPen(st::profileOfflineColor->p);
p.drawText(_left + (_width - w) / 2, top + st::btnShareContact.textTop + st::btnShareContact.font->ascent, lang(lng_profile_chat_unaccessible));
}
top += _shareContact.height();
// settings
p.setFont(st::profileHeaderFont->f);
p.setPen(st::profileHeaderColor->p);
p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_settings_section));
top += st::profileHeaderSkip;
top += _enableNotifications.height();
2014-08-15 11:19:32 +00:00
// shared media
p.setFont(st::profileHeaderFont->f);
p.setPen(st::profileHeaderColor->p);
p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_shared_media));
top += st::profileHeaderSkip;
p.setFont(st::linkFont->f);
p.setPen(st::black->p);
int oneState = 0; // < 0 - loading, 0 - no media, > 0 - link shown
for (int i = 0; i < OverviewCount; ++i) {
int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1);
if (count < 0) {
if (!oneState) oneState = count;
if (!_allMediaTypes) {
p.drawText(_left, top + st::linkFont->ascent, lang(lng_profile_loading));
break;
}
} else if (count > 0) {
oneState = count;
if (!_allMediaTypes) {
break;
}
top += _mediaLinks[i]->height() + st::setLittleSkip;
}
}
if (_allMediaTypes) {
if (oneState > 0) {
top -= st::setLittleSkip;
} else {
p.drawText(_left, top + st::linkFont->ascent, lang(oneState < 0 ? lng_profile_loading : lng_profile_no_media));
top += _mediaLinks[OverviewPhotos]->height();
}
} else {
if (!oneState) {
p.drawText(_left, top + st::linkFont->ascent, lang(lng_profile_no_media));
}
top += _mediaLinks[OverviewPhotos]->height();
}
// participants
if (_peerChat && (_peerChat->count > 0 || !_participants.isEmpty())) {
QString sectionHeader = lang(_participants.isEmpty() ? lng_profile_loading : lng_profile_participants_section);
p.setFont(st::profileHeaderFont->f);
p.setPen(st::profileHeaderColor->p);
p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, sectionHeader);
top += st::profileHeaderSkip;
int32 partfrom = top;
if (!_participants.isEmpty()) {
int32 cnt = 0, fullCnt = _participants.size();
for (Participants::const_iterator i = _participants.cbegin(), e = _participants.cend(); i != e; ++i, ++cnt) {
int32 top = partfrom + cnt * _pHeight;
if (top + _pHeight <= r.top()) continue;
if (top > r.bottom()) break;
if (_selectedRow == cnt) {
p.fillRect(_left - st::profileListPadding.width(), top, _width + 2 * st::profileListPadding.width(), _pHeight, st::profileHoverBG->b);
}
UserData *user = *i;
p.drawPixmap(_left, top + st::profileListPadding.height(), user->photo->pix(st::profileListPhotoSize));
ParticipantData *data = _participantsData[cnt];
if (!data) {
data = _participantsData[cnt] = new ParticipantData();
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
data->online = App::onlineText(user, l_time);
data->cankick = (user != App::self()) && (_chatAdmin || (_peerChat->cankick.constFind(user) != _peerChat->cankick.cend()));
}
p.setPen(st::profileListNameColor->p);
p.setFont(st::linkFont->f);
data->name.drawElided(p, _left + st::profileListPhotoSize + st::profileListPadding.width(), top + st::profileListNameTop, _width - _kickWidth - st::profileListPadding.width() - st::profileListPhotoSize - st::profileListPadding.width());
p.setFont(st::profileSubFont->f);
p.setPen((App::onlineColorUse(user->onlineTill, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
p.drawText(_left + st::profileListPhotoSize + st::profileListPadding.width(), top + st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online);
if (data->cankick) {
bool over = (user == _kickOver && (!_kickDown || _kickDown == _kickOver));
p.setFont((over ? st::linkOverFont : st::linkFont)->f);
if (user == _kickOver && _kickOver == _kickDown) {
p.setPen(st::btnDefLink.downColor->p);
} else {
p.setPen(st::btnDefLink.color->p);
}
p.drawText(_left + _width - _kickWidth, top + (_pHeight - st::linkFont->height) / 2 + st::linkFont->ascent, lang(lng_profile_kick));
}
}
top += fullCnt * _pHeight;
}
}
top += st::profileHeaderTop + st::profileHeaderFont->ascent - st::linkFont->ascent;
top += _clearHistory.height();
}
void ProfileInner::mouseMoveEvent(QMouseEvent *e) {
_lastPos = e->globalPos();
updateSelected();
bool photoOver = QRect(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize).contains(e->pos());
if (photoOver != _photoOver) {
_photoOver = photoOver;
if (!_photoLink && _peerChat && !_peerChat->forbidden) {
a_photo.start(_photoOver ? 1 : 0);
anim::start(this);
}
}
if (!_photoLink && (!_peerChat || _peerChat->forbidden)) {
setCursor((_kickOver || _kickDown) ? style::cur_pointer : style::cur_default);
} else {
setCursor((_kickOver || _kickDown || _photoOver) ? style::cur_pointer : style::cur_default);
}
}
void ProfileInner::updateSelected() {
if (!isVisible()) return;
QPoint lp = mapFromGlobal(_lastPos);
2014-08-15 11:19:32 +00:00
int32 partfrom = _mediaAudios.y() + _mediaAudios.height() + st::profileHeaderSkip;
int32 newSelected = (lp.x() >= _left - st::profileListPadding.width() && lp.x() < _left + _width + st::profileListPadding.width() && lp.y() >= partfrom) ? (lp.y() - partfrom) / _pHeight : -1;
UserData *newKickOver = 0;
if (newSelected >= 0 && newSelected < _participants.size()) {
ParticipantData *data = _participantsData[newSelected];
if (data && data->cankick) {
int32 top = partfrom + newSelected * _pHeight + (_pHeight - st::linkFont->height) / 2;
if ((lp.x() >= _left + _width - _kickWidth) && (lp.x() < _left + _width) && (lp.y() >= top) && (lp.y() < top + st::linkFont->height)) {
newKickOver = _participants[newSelected];
}
}
}
if (_kickOver != newKickOver) {
_kickOver = newKickOver;
update();
}
if (_kickDown) return;
if (newSelected != _selectedRow) {
_selectedRow = newSelected;
update();
}
}
void ProfileInner::mousePressEvent(QMouseEvent *e) {
_lastPos = e->globalPos();
updateSelected();
if (e->button() == Qt::LeftButton) {
if (_kickOver) {
_kickDown = _kickOver;
update();
} else if (_selectedRow >= 0 && _selectedRow < _participants.size()) {
App::main()->showPeerProfile(_participants[_selectedRow]);
} else if (QRect(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize).contains(e->pos())) {
if (_photoLink) {
_photoLink->onClick(e->button());
} else if (_peerChat && !_peerChat->forbidden) {
onUpdatePhoto();
}
}
}
}
void ProfileInner::mouseReleaseEvent(QMouseEvent *e) {
if (_kickDown && _kickDown == _kickOver) {
_kickConfirm = _kickOver;
ConfirmBox *box = new ConfirmBox(lng_profile_sure_kick(lt_user, _kickOver->firstName));
connect(box, SIGNAL(confirmed()), this, SLOT(onKickConfirm()));
App::wnd()->showLayer(box);
}
_kickDown = 0;
setCursor(_kickOver ? style::cur_pointer : style::cur_default);
update();
}
void ProfileInner::onKickConfirm() {
App::main()->kickParticipant(_peerChat, _kickConfirm);
}
void ProfileInner::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
2014-08-20 05:32:50 +00:00
App::main()->showBackFromStack();
}
}
void ProfileInner::enterEvent(QEvent *e) {
setMouseTracking(true);
_lastPos = QCursor::pos();
updateSelected();
return TWidget::enterEvent(e);
}
void ProfileInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
_lastPos = QCursor::pos();
updateSelected();
return TWidget::leaveEvent(e);
}
void ProfileInner::leaveToChildEvent(QEvent *e) {
_lastPos = QCursor::pos();
updateSelected();
return TWidget::leaveToChildEvent(e);
}
void ProfileInner::resizeEvent(QResizeEvent *e) {
_width = qMin(width() - st::profilePadding.left() - st::profilePadding.right(), int(st::profileMaxWidth));
_left = (width() - _width) / 2;
int32 top = 0, btnWidth = (_width - st::profileButtonSkip) / 2;
// profile
top += st::profilePadding.top();
if (_chatAdmin) {
if (_peerChat->invitationUrl.isEmpty()) {
_createInvitationLink.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop);
} else {
_createInvitationLink.move(_left + _width - _createInvitationLink.width(), top + st::profilePhoneTop);
}
if (!_invitationText.isEmpty()) {
_invitationLink.setText(st::linkFont->m.elidedText(_invitationText, Qt::ElideRight, _width - st::profilePhotoSize - st::profilePhoneLeft));
}
_invitationLink.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop + st::linkFont->height * 1.2);
_cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhotoSize - st::linkFont->height);
} else {
_cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhoneTop);
}
top += st::profilePhotoSize;
top += st::profileButtonTop;
_uploadPhoto.setGeometry(_left, top, btnWidth, _uploadPhoto.height());
_sendMessage.setGeometry(_left, top, btnWidth, _sendMessage.height());
_addParticipant.setGeometry(_left + _width - btnWidth, top, btnWidth, _addParticipant.height());
_shareContact.setGeometry(_left + _width - btnWidth, top, btnWidth, _shareContact.height());
top += _shareContact.height();
// settings
top += st::profileHeaderSkip;
_enableNotifications.move(_left, top); top += _enableNotifications.height();
2014-08-15 11:19:32 +00:00
// shared media
top += st::profileHeaderSkip;
_mediaShowAll.move(_left + _width - _mediaShowAll.width(), top);
int wasCount = 0; // < 0 - loading, 0 - no media, > 0 - link shown
for (int i = 0; i < OverviewCount; ++i) {
if (_allMediaTypes) {
int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1);
if (count > 0) {
if (wasCount) top += _mediaLinks[i]->height() + st::setLittleSkip;
wasCount = count;
}
}
_mediaLinks[i]->move(_left, top);
}
top += _mediaLinks[OverviewPhotos]->height();
// participants
if (_peerChat && (_peerChat->count > 0 || !_participants.isEmpty())) {
top += st::profileHeaderSkip;
if (!_participants.isEmpty()) {
int32 fullCnt = _participants.size();
top += fullCnt * _pHeight;
}
}
top += st::profileHeaderTop + st::profileHeaderFont->ascent - st::linkFont->ascent;
_clearHistory.move(_left, top);
}
void ProfileInner::contextMenuEvent(QContextMenuEvent *e) {
if (_menu) {
_menu->deleteLater();
_menu = 0;
}
2015-03-26 09:35:21 +00:00
if (!_phoneText.isEmpty() || (_peerUser && !_peerUser->username.isEmpty())) {
QRect info(_left + st::profilePhotoSize + st::profilePhoneLeft, st::profilePadding.top(), _width - st::profilePhotoSize - st::profilePhoneLeft, st::profilePhotoSize);
if (info.contains(mapFromGlobal(e->globalPos()))) {
_menu = new ContextMenu(this);
2015-03-26 09:35:21 +00:00
if (!_phoneText.isEmpty()) {
_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());
e->accept();
}
}
}
void ProfileInner::onMenuDestroy(QObject *obj) {
if (_menu == obj) {
_menu = 0;
}
}
void ProfileInner::onCopyPhone() {
2015-05-19 15:46:45 +00:00
DEBUG_LOG(("Setting text to clipboard from user phone: %1").arg(_phoneText));
QApplication::clipboard()->setText(_phoneText);
}
void ProfileInner::onCopyUsername() {
2015-05-19 15:46:45 +00:00
DEBUG_LOG(("Setting text to clipboard from username: @%1").arg(_peerUser->username));
QApplication::clipboard()->setText('@' + _peerUser->username);
}
bool ProfileInner::animStep(float64 ms) {
float64 dt = ms / st::setPhotoDuration;
bool res = true;
if (dt >= 1) {
res = false;
a_photo.finish();
} else {
a_photo.update(dt, anim::linear);
}
update(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize);
return res;
}
PeerData *ProfileInner::peer() const {
return _peer;
}
2014-08-15 11:19:32 +00:00
bool ProfileInner::allMediaShown() const {
return _allMediaTypes;
}
ProfileInner::~ProfileInner() {
for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) {
delete *i;
}
_participantsData.clear();
}
void ProfileInner::openContextImage() {
}
void ProfileInner::deleteContextImage() {
}
void ProfileInner::updateNotifySettings() {
_enableNotifications.setChecked(_peer->notify == EmptyNotifySettings || _peer->notify == UnknownNotifySettings || _peer->notify->mute < unixtime());
}
2014-08-15 11:19:32 +00:00
void ProfileInner::mediaOverviewUpdated(PeerData *peer) {
if (peer == _peer) {
resizeEvent(0);
showAll();
2015-03-26 09:35:21 +00:00
update();
2014-08-15 11:19:32 +00:00
}
}
void ProfileInner::showAll() {
if (_peerChat) {
_sendMessage.hide();
_shareContact.hide();
if (_peerChat->forbidden) {
_uploadPhoto.hide();
_cancelPhoto.hide();
_addParticipant.hide();
_createInvitationLink.hide();
_invitationLink.hide();
} else {
if (App::app()->isPhotoUpdating(_peer->id)) {
_uploadPhoto.hide();
_cancelPhoto.show();
} else {
_uploadPhoto.show();
_cancelPhoto.hide();
}
if (_chatAdmin) {
_createInvitationLink.show();
if (_peerChat->invitationUrl.isEmpty()) {
_invitationLink.hide();
} else {
_invitationLink.show();
}
} else {
_createInvitationLink.hide();
_invitationLink.hide();
}
if (_peerChat->count < cMaxGroupCount()) {
_addParticipant.show();
} else {
_addParticipant.hide();
}
}
_enableNotifications.show();
_clearHistory.hide();
} else {
_uploadPhoto.hide();
_cancelPhoto.hide();
_addParticipant.hide();
_createInvitationLink.hide();
_invitationLink.hide();
_sendMessage.show();
if (_peerUser->phone.isEmpty()) {
_shareContact.hide();
} else {
_shareContact.show();
}
_enableNotifications.show();
_clearHistory.show();
}
updateNotifySettings();
2014-08-15 11:19:32 +00:00
// shared media
bool first = false, wasCount = false, manyCounts = false;
for (int i = 0; i < OverviewCount; ++i) {
int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1);
if (count > 0) {
if (wasCount) {
manyCounts = true;
} else {
wasCount = true;
}
}
if (!first || _allMediaTypes) {
if (count > 0 || count < 0) {
first = true;
} else if (!_allMediaTypes) {
_mediaLinks[i]->hide();
continue;
}
if (count > 0) {
_mediaLinks[i]->setText(overviewLinkText(i, count));
_mediaLinks[i]->show();
} else {
_mediaLinks[i]->hide();
}
} else {
_mediaLinks[i]->hide();
}
}
if (_allMediaTypes || !manyCounts) {
_mediaShowAll.hide();
} else {
_mediaShowAll.show();
}
// participants
reorderParticipants();
int32 h;
if (_peerUser) {
h = _clearHistory.y() + _clearHistory.height() + st::profileHeaderSkip;
} else {
2014-08-15 11:19:32 +00:00
h = _mediaAudios.y() + _mediaAudios.height() + st::profileHeaderSkip;
if (!_participants.isEmpty()) {
h += st::profileHeaderSkip + _participants.size() * _pHeight;
} else if (_peerChat->count > 0) {
h += st::profileHeaderSkip;
}
}
resize(width(), h);
}
void ProfileInner::updateInvitationLink() {
if (!_peerChat) return;
if (_peerChat->invitationUrl.isEmpty()) {
_createInvitationLink.setText(lang(lng_group_invite_create));
} else {
_createInvitationLink.setText(lang(lng_group_invite_create_new));
_invitationText = _peerChat->invitationUrl;
if (_invitationText.startsWith(QLatin1String("http://"), Qt::CaseInsensitive)) {
_invitationText = _invitationText.mid(7);
} else if (_invitationText.startsWith(QLatin1String("https://"), Qt::CaseInsensitive)) {
_invitationText = _invitationText.mid(8);
}
}
}
2014-08-15 11:19:32 +00:00
QString ProfileInner::overviewLinkText(int32 type, int32 count) {
switch (type) {
case OverviewPhotos: return lng_profile_photos(lt_count, count);
case OverviewVideos: return lng_profile_videos(lt_count, count);
case OverviewDocuments: return lng_profile_files(lt_count, count);
case OverviewAudios: return lng_profile_audios(lt_count, count);
2014-08-15 11:19:32 +00:00
}
return QString();
2014-08-15 11:19:32 +00:00
}
ProfileWidget::ProfileWidget(QWidget *parent, const PeerData *peer) : QWidget(parent)
, _scroll(this, st::setScroll)
, _inner(this, &_scroll, peer)
, _showing(false)
{
_scroll.setWidget(&_inner);
_scroll.move(0, 0);
_inner.move(0, 0);
_scroll.show();
connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSelected()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
}
void ProfileWidget::onScroll() {
_inner.loadProfilePhotos(_scroll.scrollTop());
}
void ProfileWidget::resizeEvent(QResizeEvent *e) {
_scroll.resize(size());
_inner.resize(width(), _inner.height());
}
void ProfileWidget::mousePressEvent(QMouseEvent *e) {
}
void ProfileWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (animating() && _showing) {
p.setOpacity(a_bgAlpha.current());
p.drawPixmap(a_bgCoord.current(), 0, _bgAnimCache);
p.setOpacity(a_alpha.current());
p.drawPixmap(a_coord.current(), 0, _animCache);
} else {
p.fillRect(e->rect(), st::white->b);
}
}
void ProfileWidget::dragEnterEvent(QDragEnterEvent *e) {
}
void ProfileWidget::dropEvent(QDropEvent *e) {
}
void ProfileWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) {
if (animating() && _showing) {
p.setOpacity(a_bgAlpha.current());
p.drawPixmap(a_bgCoord.current(), 0, _bgAnimTopBarCache);
p.setOpacity(a_alpha.current());
p.drawPixmap(a_coord.current(), 0, _animTopBarCache);
} else {
p.setOpacity(st::topBarBackAlpha + (1 - st::topBarBackAlpha) * over);
p.drawPixmap(QPoint(st::topBarBackPadding.left(), (st::topBarHeight - st::topBarBackImg.pxHeight()) / 2), App::sprite(), st::topBarBackImg);
p.setFont(st::topBarBackFont->f);
p.setPen(st::topBarBackColor->p);
p.drawText(st::topBarBackPadding.left() + st::topBarBackImg.pxWidth() + st::topBarBackPadding.right(), (st::topBarHeight - st::topBarBackFont->height) / 2 + st::topBarBackFont->ascent, lang(peer()->chat ? lng_profile_group_info : lng_profile_info));
}
}
void ProfileWidget::topBarClick() {
2014-08-15 11:19:32 +00:00
App::main()->showBackFromStack();
}
PeerData *ProfileWidget::peer() const {
return _inner.peer();
}
2014-08-15 11:19:32 +00:00
int32 ProfileWidget::lastScrollTop() const {
return _scroll.scrollTop();
}
bool ProfileWidget::allMediaShown() const {
return _inner.allMediaShown();
}
void ProfileWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop, bool allMediaShown) {
stopGif();
_bgAnimCache = bgAnimCache;
_bgAnimTopBarCache = bgAnimTopBarCache;
2014-08-15 11:19:32 +00:00
if (allMediaShown) _inner.onMediaShowAll();
if (lastScrollTop >= 0) _scroll.scrollToY(lastScrollTop);
2014-06-14 19:32:11 +00:00
_animCache = myGrab(this, rect());
App::main()->topBar()->stopAnim();
_animTopBarCache = myGrab(App::main()->topBar(), QRect(0, 0, width(), st::topBarHeight));
App::main()->topBar()->startAnim();
_scroll.hide();
a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0);
a_alpha = anim::fvalue(0, 1);
a_bgCoord = back ? anim::ivalue(0, st::introSlideShift) : anim::ivalue(0, -st::introSlideShift);
a_bgAlpha = anim::fvalue(1, 0);
anim::start(this);
_showing = true;
show();
_inner.setFocus();
App::main()->topBar()->update();
}
bool ProfileWidget::animStep(float64 ms) {
float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration;
float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0;
bool res = true;
if (dt2 >= 1) {
res = _showing = false;
a_bgCoord.finish();
a_bgAlpha.finish();
a_coord.finish();
a_alpha.finish();
_bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap();
2014-06-14 19:32:11 +00:00
App::main()->topBar()->stopAnim();
_scroll.show();
activate();
} else {
a_bgCoord.update(dt1, st::introHideFunc);
a_bgAlpha.update(dt1, st::introAlphaHideFunc);
a_coord.update(dt2, st::introShowFunc);
a_alpha.update(dt2, st::introAlphaShowFunc);
}
update();
App::main()->topBar()->update();
return res;
}
void ProfileWidget::updateOnlineDisplay() {
_inner.updateOnlineDisplay();
updateOnlineDisplayTimer();
}
void ProfileWidget::updateOnlineDisplayTimer() {
_inner.updateOnlineDisplayTimer();
}
void ProfileWidget::updateNotifySettings() {
_inner.updateNotifySettings();
}
2014-08-15 11:19:32 +00:00
void ProfileWidget::mediaOverviewUpdated(PeerData *peer) {
_inner.mediaOverviewUpdated(peer);
}
ProfileWidget::~ProfileWidget() {
}
void ProfileWidget::activate() {
_inner.setFocus();
}