version 0.7.21.dev prepared - replies, mentions

This commit is contained in:
John Preston 2015-03-19 12:18:19 +03:00
parent 39acdd8725
commit 1f7e39e184
45 changed files with 5860 additions and 3676 deletions

View file

@ -91,6 +91,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_server_error" = "Internal server error."; "lng_server_error" = "Internal server error.";
"lng_flood_error" = "Too much tries. Please try again later."; "lng_flood_error" = "Too much tries. Please try again later.";
"lng_deleted" = "Unknown"; "lng_deleted" = "Unknown";
"lng_deleted_message" = "Deleted message";
"lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b]."; "lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b].";
"lng_start_msgs" = "START MESSAGING"; "lng_start_msgs" = "START MESSAGING";
@ -332,6 +333,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} created group «{title}»"; "lng_action_created_chat" = "{from} created group «{title}»";
"lng_forwarded_from" = "Forwarded from"; "lng_forwarded_from" = "Forwarded from";
"lng_in_reply_to" = "In reply to";
"lng_attach_failed" = "Failed"; "lng_attach_failed" = "Failed";
"lng_attach_file" = "File"; "lng_attach_file" = "File";
@ -385,6 +387,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "Copy email address"; "lng_context_copy_email" = "Copy email address";
"lng_context_open_hashtag" = "Search by hashtag"; "lng_context_open_hashtag" = "Search by hashtag";
"lng_context_copy_hashtag" = "Copy hashtag"; "lng_context_copy_hashtag" = "Copy hashtag";
"lng_context_open_mention" = "Open {user} profile";
"lng_context_copy_mention" = "Copy mention";
"lng_context_open_image" = "Open Image"; "lng_context_open_image" = "Open Image";
"lng_context_save_image" = "Save Image As.."; "lng_context_save_image" = "Save Image As..";
"lng_context_forward_image" = "Forward Image"; "lng_context_forward_image" = "Forward Image";
@ -403,8 +407,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Forward File"; "lng_context_forward_file" = "Forward File";
"lng_context_delete_file" = "Delete File"; "lng_context_delete_file" = "Delete File";
"lng_context_close_file" = "Close File"; "lng_context_close_file" = "Close File";
"lng_context_copy_text" = "Copy Message Text"; "lng_context_copy_text" = "Copy Text";
"lng_context_to_msg" = "Go To Message"; "lng_context_to_msg" = "Go To Message";
"lng_context_reply_msg" = "Reply";
"lng_context_forward_msg" = "Forward Message"; "lng_context_forward_msg" = "Forward Message";
"lng_context_delete_msg" = "Delete Message"; "lng_context_delete_msg" = "Delete Message";
"lng_context_select_msg" = "Select Message"; "lng_context_select_msg" = "Select Message";
@ -425,6 +430,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "Send selected files to {recipient}?"; "lng_forward_send_files_confirm" = "Send selected files to {recipient}?";
"lng_forward" = "Forward"; "lng_forward" = "Forward";
"lng_forward_send" = "Send"; "lng_forward_send" = "Send";
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
"lng_contact_phone" = "Phone number"; "lng_contact_phone" = "Phone number";
"lng_enter_contact_data" = "New Contact"; "lng_enter_contact_data" = "New Contact";

View file

@ -784,6 +784,8 @@ msgInSelectOverlay: #358cd44c;
msgStickerOverlay: #358cd47f; msgStickerOverlay: #358cd47f;
msgOutServiceColor: #3a8e26; msgOutServiceColor: #3a8e26;
msgInServiceColor: #0e7acd; msgInServiceColor: #0e7acd;
msgOutServiceSelColor: #367570;
msgInServiceSelColor: #0e7acd;
msgShadow: 2px; msgShadow: 2px;
msgInShadow: #748ea229; msgInShadow: #748ea229;
msgOutShadow: #3ac34740; msgOutShadow: #3ac34740;
@ -794,6 +796,15 @@ msgOutDateColor: #6cc264;
msgInSelectDateColor: #6a9cc5; msgInSelectDateColor: #6a9cc5;
msgOutSelectDateColor: #50a79c; msgOutSelectDateColor: #50a79c;
msgReplyPadding: margins(6px, 6px, 11px, 6px);
msgReplyBarPos: point(1px, 0px);
msgReplyBarSize: size(2px, 36px);
msgReplyBarSkip: 10px;
msgOutReplyBarColor: #5dc452;
msgInReplyBarColor: #2fa9e2;
msgOutReplyBarSelColor: #4da79f;
msgInReplyBarSelColor: #2fa9e2;
msgServiceSelectBG: #fff4; msgServiceSelectBG: #fff4;
msgServiceRadius: 2px; msgServiceRadius: 2px;
@ -878,7 +889,7 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) {
mediaMaxWidth: 250px; mediaMaxWidth: 250px;
mediaFont: font(fsize); mediaFont: font(fsize);
mediaPadding: margins(7px, 6px, 11px, 6px); mediaPadding: margins(7px, 6px, 7px, 6px);
mediaThumbSize: 48px; mediaThumbSize: 48px;
mediaNameTop: 3px; mediaNameTop: 3px;
mediaDetailsShift: 3px; mediaDetailsShift: 3px;
@ -960,6 +971,24 @@ btnAttachEmoji: iconedButton(btnAttachDocument) {
width: 32px; width: 32px;
} }
replySkip: 52px;
replyColor: #377aae;
replyHeight: 49px;
replyTop: 8px;
replyBottom: 6px;
replyIconPos: point(13px, 15px);
replyIcon: sprite(174px, 195px, 24px, 24px);
replyCancel: iconedButton(btnDefIconed) {
icon: sprite(165px, 24px, 14px, 14px);
iconPos: point(17px, 17px);
downIcon: sprite(165px, 24px, 14px, 14px);
downIconPos: point(17px, 18px);
bgColor: white;
overBgColor: white;
width: 49px;
height: 49px;
}
historyScroll: flatScroll(scrollDef) { historyScroll: flatScroll(scrollDef) {
barColor: #89a0b47a; barColor: #89a0b47a;
bgColor: #89a0b44c; bgColor: #89a0b44c;
@ -1318,6 +1347,7 @@ dropdownShadow: sprite(241px, 46px, 6px, 6px);
dropdownBorder: 1px; dropdownBorder: 1px;
dropdownBorderColor: #ebebeb; dropdownBorderColor: #ebebeb;
dropdownBackground: white; dropdownBackground: white;
dropdownDuration: 150;
dropdownAttachDocument: iconedButton(btnAttachDocument) { dropdownAttachDocument: iconedButton(btnAttachDocument) {
iconPos: point(14px, 13px); iconPos: point(14px, 13px);
@ -1714,3 +1744,13 @@ passcodeSubmit: flatButton(btnIntroNext) {
font: font(19px); font: font(19px);
overFont: font(19px); overFont: font(19px);
} }
mentionHeight: 40px;
mentionScroll: flatScroll(scrollDef) {
topsh: 0;
bottomsh: 0;
}
mentionPadding: margins(8px, 5px, 8px, 5px);
mentionTop: 11px;
mentionFont: linkFont;
mentionPhotoSize: msgPhotoSize;

View file

@ -0,0 +1,181 @@
/*
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 "style.h"
#include "lang.h"
#include "application.h"
#include "window.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "localstorage.h"
ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
App::initBackground();
connect(&_replyToTimer, SIGNAL(timeout()), this, SLOT(resolveReplyTo()));
}
void ApiWrap::init() {
App::initMedia();
}
void ApiWrap::itemRemoved(HistoryItem *item) {
if (HistoryReply *reply = item->toHistoryReply()) {
ReplyToRequests::iterator i = _replyToRequests.find(reply->replyToId());
if (i != _replyToRequests.cend()) {
for (QList<HistoryReply*>::iterator j = i->replies.begin(); j != i->replies.end();) {
if ((*j) == reply) {
j = i->replies.erase(j);
} else {
++j;
}
}
if (i->replies.isEmpty()) {
_replyToRequests.erase(i);
}
}
}
}
void ApiWrap::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (HistoryReply *reply = oldItem->toHistoryReply()) {
ReplyToRequests::iterator i = _replyToRequests.find(reply->replyToId());
if (i != _replyToRequests.cend()) {
for (QList<HistoryReply*>::iterator j = i->replies.begin(); j != i->replies.end();) {
if ((*j) == reply) {
if (HistoryReply *newReply = newItem->toHistoryReply()) {
*j = newReply;
++j;
} else {
j = i->replies.erase(j);
}
} else {
++j;
}
}
if (i->replies.isEmpty()) {
_replyToRequests.erase(i);
}
}
}
}
void ApiWrap::requestReplyTo(HistoryReply *reply, MsgId to) {
ReplyToRequest &req(_replyToRequests[to]);
req.replies.append(reply);
if (!req.req) _replyToTimer.start(1);
}
void ApiWrap::requestFullPeer(PeerData *peer) {
if (_fullRequests.contains(peer)) return;
mtpRequestId req;
if (peer->chat) {
req = MTP::send(MTPmessages_GetFullChat(MTP_int(App::chatFromPeer(peer->id))), rpcDone(&ApiWrap::gotChatFull, peer), rpcFail(&ApiWrap::gotPeerFailed, peer));
} else {
req = MTP::send(MTPusers_GetFullUser(peer->asUser()->inputUser), rpcDone(&ApiWrap::gotUserFull, peer), rpcFail(&ApiWrap::gotPeerFailed, peer));
}
_fullRequests.insert(peer, req);
}
void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
const MTPDmessages_chatFull &d(result.c_messages_chatFull());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
App::feedParticipants(d.vfull_chat.c_chatFull().vparticipants);
PhotoData *photo = App::feedPhoto(d.vfull_chat.c_chatFull().vchat_photo);
if (photo) {
ChatData *chat = peer->asChat();
if (chat) {
chat->photoId = photo->id;
photo->chat = chat;
}
}
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vfull_chat.c_chatFull().vnotify_settings);
_fullRequests.remove(peer);
emit fullPeerLoaded(peer);
}
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
const MTPDuserFull &d(result.c_userFull());
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
App::feedUserLink(MTP_int(App::userFromPeer(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
_fullRequests.remove(peer);
emit fullPeerLoaded(peer);
}
bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &err) {
_fullRequests.remove(peer);
return true;
}
void ApiWrap::resolveReplyTo() {
if (_replyToRequests.isEmpty()) return;
QVector<MTPint> ids;
ids.reserve(_replyToRequests.size());
for (ReplyToRequests::const_iterator i = _replyToRequests.cbegin(), e = _replyToRequests.cend(); i != e; ++i) {
if (!i.value().req) {
ids.push_back(MTP_int(i.key()));
}
}
if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotReplyTo));
for (ReplyToRequests::iterator i = _replyToRequests.begin(), e = _replyToRequests.end(); i != e; ++i) {
i.value().req = req;
}
}
}
void ApiWrap::gotReplyTo(const MTPmessages_Messages &msgs, mtpRequestId req) {
switch (msgs.type()) {
case mtpc_messages_messages:
App::feedUsers(msgs.c_messages_messages().vusers);
App::feedChats(msgs.c_messages_messages().vchats);
App::feedMsgs(msgs.c_messages_messages().vmessages, -1);
break;
case mtpc_messages_messagesSlice:
App::feedUsers(msgs.c_messages_messagesSlice().vusers);
App::feedChats(msgs.c_messages_messagesSlice().vchats);
App::feedMsgs(msgs.c_messages_messagesSlice().vmessages, -1);
break;
}
for (ReplyToRequests::iterator i = _replyToRequests.begin(); i != _replyToRequests.cend();) {
if (i.value().req == req) {
for (QList<HistoryReply*>::const_iterator j = i.value().replies.cbegin(), e = i.value().replies.cend(); j != e; ++j) {
if (*j) {
(*j)->updateReplyTo(true);
} else {
App::main()->updateReplyTo();
}
}
i = _replyToRequests.erase(i);
} else {
++i;
}
}
}
ApiWrap::~ApiWrap() {
App::deinitMedia(false);
}

View file

@ -0,0 +1,64 @@
/*
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
*/
#pragma once
class ApiWrap : public QObject, public RPCSender {
Q_OBJECT
public:
ApiWrap(QObject *parent);
void init();
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void requestReplyTo(HistoryReply *reply, MsgId to);
void requestFullPeer(PeerData *peer);
~ApiWrap();
signals:
void fullPeerLoaded(PeerData *peer);
public slots:
void resolveReplyTo();
private:
void gotReplyTo(const MTPmessages_Messages &result, mtpRequestId req);
struct ReplyToRequest {
ReplyToRequest() : req(0) {
}
mtpRequestId req;
QList<HistoryReply*> replies;
};
typedef QMap<MsgId, ReplyToRequest> ReplyToRequests;
ReplyToRequests _replyToRequests;
SingleTimer _replyToTimer;
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result);
void gotUserFull(PeerData *peer, const MTPUserFull &result);
bool gotPeerFailed(PeerData *peer, const RPCError &err);
typedef QMap<PeerData*, mtpRequestId> FullRequests;
FullRequests _fullRequests;
};

View file

@ -52,6 +52,8 @@ namespace {
VideoItems videoItems; VideoItems videoItems;
AudioItems audioItems; AudioItems audioItems;
DocumentItems documentItems; DocumentItems documentItems;
typedef QMap<HistoryItem*, QMap<HistoryReply*, bool> > RepliesTo;
RepliesTo repliesTo;
Histories histories; Histories histories;
@ -118,6 +120,10 @@ namespace App {
return app() ? app()->uploader() : 0; return app() ? app()->uploader() : 0;
} }
ApiWrap *api() {
return main() ? main()->api() : 0;
}
void showSettings() { void showSettings() {
Window *w(wnd()); Window *w(wnd());
if (w) w->showSettings(); if (w) w->showSettings();
@ -590,7 +596,7 @@ namespace App {
} }
} }
void feedMsgs(const MTPVector<MTPMessage> &msgs, bool newMsgs) { void feedMsgs(const MTPVector<MTPMessage> &msgs, int msgsState) {
const QVector<MTPMessage> &v(msgs.c_vector().v); const QVector<MTPMessage> &v(msgs.c_vector().v);
QMap<int32, int32> msgsIds; QMap<int32, int32> msgsIds;
for (int32 i = 0, l = v.size(); i < l; ++i) { for (int32 i = 0, l = v.size(); i < l; ++i) {
@ -602,7 +608,7 @@ namespace App {
} }
} }
for (QMap<int32, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) { for (QMap<int32, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
histories().addToBack(v[*i], newMsgs ? 1 : 0); histories().addToBack(v[*i], msgsState);
} }
} }
@ -1253,6 +1259,22 @@ namespace App {
} }
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) { void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (HistoryReply *r = oldItem->toHistoryReply()) {
QMap<HistoryReply*, bool> &replies(::repliesTo[r->replyToMessage()]);
replies.remove(r);
if (HistoryReply *n = newItem->toHistoryReply()) {
replies.insert(n, true);
}
}
RepliesTo::iterator i = ::repliesTo.find(oldItem);
if (i != ::repliesTo.cend() && oldItem != newItem) {
QMap<HistoryReply*, bool> replies = i.value();
::repliesTo.erase(i);
::repliesTo[newItem] = replies;
for (QMap<HistoryReply*, bool>::iterator i = replies.begin(), e = replies.end(); i != e; ++i) {
i.key()->replyToReplaced(oldItem, newItem);
}
}
newItem->history()->itemReplaced(oldItem, newItem); newItem->history()->itemReplaced(oldItem, newItem);
if (App::main()) App::main()->itemReplaced(oldItem, newItem); if (App::main()) App::main()->itemReplaced(oldItem, newItem);
if (App::hoveredItem() == oldItem) App::hoveredItem(newItem); if (App::hoveredItem() == oldItem) App::hoveredItem(newItem);
@ -1311,6 +1333,13 @@ namespace App {
} }
} }
historyItemDetached(item); historyItemDetached(item);
RepliesTo::iterator j = ::repliesTo.find(item);
if (j != ::repliesTo.cend()) {
for (QMap<HistoryReply*, bool>::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->replyToReplaced(item, 0);
}
::repliesTo.erase(j);
}
if (App::main() && !App::quiting()) { if (App::main() && !App::quiting()) {
App::main()->itemRemoved(item); App::main()->itemRemoved(item);
} }
@ -1361,12 +1390,28 @@ namespace App {
::videoItems.clear(); ::videoItems.clear();
::audioItems.clear(); ::audioItems.clear();
::documentItems.clear(); ::documentItems.clear();
::repliesTo.clear();
lastPhotos.clear(); lastPhotos.clear();
lastPhotosMap.clear(); lastPhotosMap.clear();
::self = 0; ::self = 0;
if (App::wnd()) App::wnd()->updateGlobalMenu(); if (App::wnd()) App::wnd()->updateGlobalMenu();
} }
/* // don't delete history without deleting its' peerdata
void historyRegReply(HistoryReply *reply, HistoryItem *to) {
::repliesTo[to].insert(reply, true);
}
void historyUnregReply(HistoryReply *reply, HistoryItem *to) {
RepliesTo::iterator i = ::repliesTo.find(to);
if (i != ::repliesTo.cend()) {
i.value().remove(reply);
if (i.value().isEmpty()) {
::repliesTo.erase(i);
}
}
}
/* // don't delete history without deleting its' peerdata
void deleteHistory(const PeerId &peer) { void deleteHistory(const PeerId &peer) {
Histories::iterator i = ::histories.find(peer); Histories::iterator i = ::histories.find(peer);
if (i != ::histories.end()) { if (i != ::histories.end()) {
@ -1661,9 +1706,9 @@ namespace App {
} }
} }
void openUserByName(const QString &username) { void openUserByName(const QString &username, bool toProfile) {
if (App::main()) { if (App::main()) {
App::main()->openUserByName(username); App::main()->openUserByName(username, toProfile);
} }
} }

View file

@ -23,6 +23,7 @@ class Application;
class Window; class Window;
class MainWidget; class MainWidget;
class SettingsWidget; class SettingsWidget;
class ApiWrap;
class Font; class Font;
class Color; class Color;
class FileUploader; class FileUploader;
@ -41,6 +42,7 @@ namespace App {
SettingsWidget *settings(); SettingsWidget *settings();
bool passcoded(); bool passcoded();
FileUploader *uploader(); FileUploader *uploader();
ApiWrap *api();
void showSettings(); void showSettings();
void logOut(); void logOut();
@ -74,7 +76,7 @@ namespace App {
void feedParticipants(const MTPChatParticipants &p); void feedParticipants(const MTPChatParticipants &p);
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d); void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d); void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
void feedMsgs(const MTPVector<MTPMessage> &msgs, bool newMsgs = false); void feedMsgs(const MTPVector<MTPMessage> &msgs, int msgsState = 0); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message
void feedWereRead(const QVector<MTPint> &msgsIds); void feedWereRead(const QVector<MTPint> &msgsIds);
void feedInboxRead(const PeerId &peer, int32 upTo); void feedInboxRead(const PeerId &peer, int32 upTo);
void feedOutboxRead(const PeerId &peer, int32 upTo); void feedOutboxRead(const PeerId &peer, int32 upTo);
@ -127,6 +129,8 @@ namespace App {
void historyUnregItem(HistoryItem *item); void historyUnregItem(HistoryItem *item);
void historyClearMsgs(); void historyClearMsgs();
void historyClearItems(); void historyClearItems();
void historyRegReply(HistoryReply *reply, HistoryItem *to);
void historyUnregReply(HistoryReply *reply, HistoryItem *to);
// void deleteHistory(const PeerId &peer); // void deleteHistory(const PeerId &peer);
void historyRegRandom(uint64 randomId, MsgId itemId); void historyRegRandom(uint64 randomId, MsgId itemId);
@ -181,7 +185,7 @@ namespace App {
void setProxySettings(QTcpSocket &socket); void setProxySettings(QTcpSocket &socket);
void searchByHashtag(const QString &tag); void searchByHashtag(const QString &tag);
void openUserByName(const QString &username); void openUserByName(const QString &username, bool toProfile = false);
void openLocalUrl(const QString &url); void openLocalUrl(const QString &url);
void initBackground(int32 id = 0, const QImage &p = QImage(), bool nowrite = false); void initBackground(int32 id = 0, const QImage &p = QImage(), bool nowrite = false);

View file

@ -478,7 +478,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
int32 filesize = 0; int32 filesize = 0;
QByteArray data; QByteArray data;
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false); ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection); connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
@ -671,30 +671,28 @@ void Application::startApp() {
DEBUG_LOG(("Application Info: starting app..")); DEBUG_LOG(("Application Info: starting app.."));
Local::ReadMapState state = Local::readMap(QByteArray());
if (state == Local::ReadMapPassNeeded) {
cSetHasPasscode(true);
}
DEBUG_LOG(("Application Info: local map read.."));
window->createWinId(); window->createWinId();
window->init(); window->init();
DEBUG_LOG(("Application Info: window created..")); DEBUG_LOG(("Application Info: window created.."));
if (state != Local::ReadMapPassNeeded) { initImageLinkManager();
App::initMedia();
Local::ReadMapState state = Local::readMap(QByteArray());
if (state == Local::ReadMapPassNeeded) {
cSetHasPasscode(true);
DEBUG_LOG(("Application Info: passcode nneded.."));
} else {
DEBUG_LOG(("Application Info: local map read.."));
MTP::start(); MTP::start();
} }
MTP::setStateChangedHandler(mtpStateChanged); MTP::setStateChangedHandler(mtpStateChanged);
MTP::setSessionResetHandler(mtpSessionReset); MTP::setSessionResetHandler(mtpSessionReset);
DEBUG_LOG(("Application Info: MTP started..")); DEBUG_LOG(("Application Info: MTP started.."));
initImageLinkManager();
App::initMedia();
DEBUG_LOG(("Application Info: showing.")); DEBUG_LOG(("Application Info: showing."));
if (state == Local::ReadMapPassNeeded) { if (state == Local::ReadMapPassNeeded) {
window->setupPasscode(false); window->setupPasscode(false);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View file

@ -29,6 +29,7 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(new ReadyLocalMedi
_compressed(this, lang(lng_send_image_compressed), cCompressPastedImage()), _compressed(this, lang(lng_send_image_compressed), cCompressPastedImage()),
_sendButton(this, lang(lng_send_button), st::btnSelectDone), _sendButton(this, lang(lng_send_button), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel), _cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_replyTo(img.replyTo),
a_opacity(0, 1) { a_opacity(0, 1) {
connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel())); connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
@ -95,12 +96,12 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(new ReadyLocalMedi
resize(_width, _height); resize(_width, _height);
} }
PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QString &lname) : _img(0), PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo) : _img(0),
_thumbx(0), _thumby(0), _thumbw(0), _thumbh(0), _namew(0), _textw(0), _thumbx(0), _thumby(0), _thumbw(0), _thumbh(0), _namew(0), _textw(0),
_compressed(this, lang(lng_send_image_compressed), true), _compressed(this, lang(lng_send_image_compressed), true),
_sendButton(this, lang(lng_send_button), st::btnSelectDone), _sendButton(this, lang(lng_send_button), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel), _cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_phone(phone), _fname(fname), _lname(lname), _phone(phone), _fname(fname), _lname(lname), _replyTo(replyTo),
a_opacity(0, 1) { a_opacity(0, 1) {
connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel())); connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
@ -203,7 +204,7 @@ void PhotoSendBox::animStep(float64 ms) {
void PhotoSendBox::onSend(bool ctrlShiftEnter) { void PhotoSendBox::onSend(bool ctrlShiftEnter) {
if (!_img) { if (!_img) {
if (App::main()) App::main()->confirmShareContact(ctrlShiftEnter, _phone, _fname, _lname); if (App::main()) App::main()->confirmShareContact(ctrlShiftEnter, _phone, _fname, _lname, _replyTo);
} else { } else {
if (!_compressed.isHidden()) { if (!_compressed.isHidden()) {
if (_compressed.checked() != cCompressPastedImage()) { if (_compressed.checked() != cCompressPastedImage()) {
@ -215,7 +216,7 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) {
_img->ctrlShiftEnter = ctrlShiftEnter; _img->ctrlShiftEnter = ctrlShiftEnter;
if (App::main()) App::main()->confirmSendImage(*_img); if (App::main()) App::main()->confirmSendImage(*_img);
} else { } else {
if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter); if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter, _replyTo);
} }
} }
emit closed(); emit closed();

View file

@ -26,7 +26,7 @@ class PhotoSendBox : public LayeredWidget {
public: public:
PhotoSendBox(const ReadyLocalMedia &img); PhotoSendBox(const ReadyLocalMedia &img);
PhotoSendBox(const QString &phone, const QString &fname, const QString &lname); PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo);
void parentResized(); void parentResized();
void animStep(float64 ms); void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e); void keyPressEvent(QKeyEvent *e);
@ -50,6 +50,7 @@ private:
QPixmap _thumb; QPixmap _thumb;
QString _phone, _fname, _lname; QString _phone, _fname, _lname;
MsgId _replyTo;
anim::fvalue a_opacity; anim::fvalue a_opacity;

View file

@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
static const int32 AppVersion = 7020; static const int32 AppVersion = 7021;
static const wchar_t *AppVersionStr = L"0.7.20"; static const wchar_t *AppVersionStr = L"0.7.21";
static const bool DevChannel = false; static const bool DevChannel = true;
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop"; static const wchar_t *AppName = L"Telegram Desktop";

View file

@ -24,6 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "lang.h" #include "lang.h"
#include "window.h" #include "window.h"
#include "apiwrap.h"
Dropdown::Dropdown(QWidget *parent) : TWidget(parent), Dropdown::Dropdown(QWidget *parent) : TWidget(parent),
_hiding(false), a_opacity(0), _shadow(st::dropdownShadow) { _hiding(false), a_opacity(0), _shadow(st::dropdownShadow) {
@ -160,7 +161,7 @@ void Dropdown::showStart() {
} }
bool Dropdown::animStep(float64 ms) { bool Dropdown::animStep(float64 ms) {
float64 dt = ms / 150; float64 dt = ms / st::dropdownDuration;
bool res = true; bool res = true;
if (dt >= 1) { if (dt >= 1) {
a_opacity.finish(); a_opacity.finish();
@ -311,7 +312,7 @@ void DragArea::showStart() {
} }
bool DragArea::animStep(float64 ms) { bool DragArea::animStep(float64 ms) {
float64 dt = ms / 150; float64 dt = ms / st::dropdownDuration;
bool res = true; bool res = true;
if (dt >= 1) { if (dt >= 1) {
a_opacity.finish(); a_opacity.finish();
@ -764,7 +765,7 @@ void EmojiPan::fastHide() {
} }
bool EmojiPan::animStep(float64 ms) { bool EmojiPan::animStep(float64 ms) {
float64 dt = ms / 150; float64 dt = ms / st::dropdownDuration;
bool res = true; bool res = true;
if (dt >= 1) { if (dt >= 1) {
a_opacity.finish(); a_opacity.finish();
@ -880,6 +881,341 @@ void EmojiPan::onTabChange() {
} }
} }
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows) : _parent(parent), _rows(rows), _sel(-1), _mouseSel(false) {
}
void MentionsInner::paintEvent(QPaintEvent *e) {
QPainter p(this);
int32 atwidth = st::mentionFont->m.width('@'), availwidth = width() - 2 * st::mentionPadding.left() - st::mentionPhotoSize - 2 * st::mentionPadding.right();
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1, last = _rows->size();
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
if (i == _sel) p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::dlgHoverBG->b);
UserData *user = _rows->at(last - i - 1);
QString uname = user->username;
int32 unamewidth = atwidth + st::mentionFont->m.width(uname), 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;
}
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.fillRect(cWideMode() ? st::dlgShadow : 0, _parent->innerTop(), width() - (cWideMode() ? st::dlgShadow : 0), st::titleShadow, st::titleShadowColor->b);
p.fillRect(cWideMode() ? st::dlgShadow : 0, _parent->innerBottom() - st::titleShadow, width() - (cWideMode() ? st::dlgShadow : 0), st::titleShadow, st::titleShadowColor->b);
}
void MentionsInner::mouseMoveEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
}
void MentionsInner::clearSel() {
_mouseSel = false;
setSel(-1);
}
bool MentionsInner::moveSel(int direction) {
_mouseSel = false;
if (_sel >= _rows->size() || _sel < 0) {
if (direction < 0) setSel(_rows->size() - 1, true);
return (_sel >= 0 && _sel < _rows->size());
}
if (_sel > 0 || direction > 0) {
setSel((_sel + direction >= _rows->size()) ? -1 : (_sel + direction), true);
}
return true;
}
bool MentionsInner::select() {
if (_sel >= 0 && _sel < _rows->size()) {
emit mentioned(_rows->at(_rows->size() - _sel - 1)->username);
return true;
}
return false;
}
void MentionsInner::mousePressEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
if (e->button() == Qt::LeftButton) {
select();
}
}
void MentionsInner::enterEvent(QEvent *e) {
setMouseTracking(true);
_mousePos = QCursor::pos();
onUpdateSelected(true);
}
void MentionsInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
if (_sel >= 0) {
setSel(-1);
}
}
void MentionsInner::setSel(int sel, bool scroll) {
_sel = sel;
parentWidget()->update();
if (scroll && _sel >= 0 && _sel < _rows->size()) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
}
void MentionsInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(_mousePos));
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
int w = width(), mouseY = mouse.y();
int32 sel = mouseY / int32(st::mentionHeight);
if (sel < 0 || sel >= _rows->size()) {
sel = -1;
}
if (sel != _sel) {
setSel(sel);
}
}
void MentionsInner::onParentGeometryChanged() {
_mousePos = QCursor::pos();
if (rect().contains(mapFromGlobal(_mousePos))) {
setMouseTracking(true);
onUpdateSelected(true);
}
}
MentionsDropdown::MentionsDropdown(QWidget *parent) : QWidget(parent),
_scroll(this, st::mentionScroll), _inner(this, &_rows), _chat(0), _hiding(false), a_opacity(0), _shadow(st::dropdownShadow) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_inner, SIGNAL(mentioned(QString)), this, SIGNAL(mentioned(QString)));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
setFocusPolicy(Qt::NoFocus);
_scroll.setFocusPolicy(Qt::NoFocus);
_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
_inner.setGeometry(rect());
_scroll.setGeometry(rect());
_scroll.setWidget(&_inner);
_scroll.show();
_inner.show();
connect(&_scroll, SIGNAL(geometryChanged()), &_inner, SLOT(onParentGeometryChanged()));
connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(onUpdateSelected()));
if (cPlatform() == dbipMac) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
}
void MentionsDropdown::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
p.fillRect(rect(), st::white->b);
}
void MentionsDropdown::showFiltered(ChatData *chat, QString start) {
_chat = chat;
start = start.toLower();
bool toDown = (_filter != start);
if (toDown) {
_filter = start;
}
int32 now = unixtime();
QMultiMap<int32, UserData*> ordered;
MentionRows rows;
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
if (_chat->participants.isEmpty()) {
if (_chat->count > 0) {
App::api()->requestFullPeer(_chat);
}
} else {
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (user->username.isEmpty()) continue;
if (!_filter.isEmpty() && !user->username.startsWith(_filter, Qt::CaseInsensitive)) continue;
ordered.insertMulti(App::onlineForSort(user->onlineTill, now), user);
}
}
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (!_filter.isEmpty() && !user->username.startsWith(_filter, Qt::CaseInsensitive)) continue;
rows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user->onlineTill, now), user);
}
}
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
rows.push_back(i.value());
}
}
if (rows.isEmpty()) {
if (!isHidden()) {
hideStart();
_rows.clear();
}
} else {
_rows = rows;
bool hidden = _hiding || isHidden();
if (hidden) {
show();
_scroll.show();
}
recount(toDown);
if (hidden) {
hide();
showStart();
}
}
}
void MentionsDropdown::setBoundings(QRect boundings) {
_boundings = boundings;
resize(_boundings.width(), height());
_scroll.resize(size());
_inner.resize(width(), _inner.height());
recount();
}
void MentionsDropdown::recount(bool toDown) {
int32 h = _rows.size() * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
if (_inner.height() != h) {
st += h - _inner.height();
_inner.resize(width(), h);
}
if (h > _boundings.height()) h = _boundings.height();
if (h > 5 * st::mentionHeight) h = 5 * st::mentionHeight;
if (height() != h) {
st += _scroll.height() - h;
setGeometry(0, _boundings.height() - h, width(), h);
_scroll.resize(width(), h);
} else if (y() != _boundings.height() - h) {
move(0, _boundings.height() - h);
}
if (toDown) st = _scroll.scrollTopMax();
if (st != oldst) _scroll.scrollToY(st);
if (toDown) _inner.clearSel();
}
void MentionsDropdown::fastHide() {
if (animating()) {
anim::stop(this);
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
hideFinish();
}
void MentionsDropdown::hideStart() {
if (!_hiding) {
if (_cache.isNull()) {
_scroll.show();
_cache = myGrab(this, rect());
}
_scroll.hide();
_hiding = true;
a_opacity.start(0);
anim::start(this);
}
}
void MentionsDropdown::hideFinish() {
hide();
_hiding = false;
_filter = qsl("-");
_inner.clearSel();
}
void MentionsDropdown::showStart() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) {
return;
}
if (_cache.isNull()) {
_scroll.show();
_cache = myGrab(this, rect());
}
_scroll.hide();
_hiding = false;
show();
a_opacity.start(1);
anim::start(this);
}
bool MentionsDropdown::animStep(float64 ms) {
float64 dt = ms / st::dropdownDuration;
bool res = true;
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (_hiding) {
hideFinish();
} else {
_scroll.show();
_inner.clearSel();
}
res = false;
} else {
a_opacity.update(dt, anim::linear);
}
update();
return res;
}
int32 MentionsDropdown::innerTop() {
return _scroll.scrollTop();
}
int32 MentionsDropdown::innerBottom() {
return _scroll.scrollTop() + _scroll.height();
}
bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
if (isHidden()) return QWidget::eventFilter(obj, e);
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
if (ev->key() == Qt::Key_Up) {
_inner.moveSel(-1);
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) {
return _inner.select();
}
}
return QWidget::eventFilter(obj, e);
}
MentionsDropdown::~MentionsDropdown() {
}
//StickerPanInner::StickerPanInner(QWidget *parent) : QWidget(parent), _emoji(0), _selected(-1), _pressedSel(-1) { //StickerPanInner::StickerPanInner(QWidget *parent) : QWidget(parent), _emoji(0), _selected(-1), _pressedSel(-1) {
// resize(StickerPadPerRow * st::stickerPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub); // resize(StickerPadPerRow * st::stickerPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
// setMouseTracking(true); // setMouseTracking(true);
@ -1124,7 +1460,7 @@ void EmojiPan::onTabChange() {
//} //}
// //
//bool StickerPan::animStep(float64 ms) { //bool StickerPan::animStep(float64 ms) {
// float64 dt = ms / 150; // float64 dt = ms / st::dropdownDuration;
// bool res = true; // bool res = true;
// if (dt >= 1) { // if (dt >= 1) {
// a_opacity.finish(); // a_opacity.finish();

View file

@ -227,6 +227,109 @@ private:
}; };
typedef QList<UserData*> MentionRows;
class MentionsDropdown;
class MentionsInner : public QWidget {
Q_OBJECT
public:
MentionsInner(MentionsDropdown *parent, MentionRows *rows);
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void clearSel();
bool moveSel(int direction);
bool select();
signals:
void mentioned(QString username);
void mustScrollTo(int scrollToTop, int scrollToBottom);
public slots:
void onParentGeometryChanged();
void onUpdateSelected(bool force = false);
private:
void setSel(int sel, bool scroll = false);
MentionsDropdown *_parent;
MentionRows *_rows;
int32 _sel;
bool _mouseSel;
QPoint _mousePos;
};
class MentionsDropdown : public QWidget, public Animated {
Q_OBJECT
public:
MentionsDropdown(QWidget *parent);
void paintEvent(QPaintEvent *e);
void fastHide();
void showFiltered(ChatData *chat, QString start);
void setBoundings(QRect boundings);
bool animStep(float64 ms);
int32 innerTop();
int32 innerBottom();
bool eventFilter(QObject *obj, QEvent *e);
~MentionsDropdown();
signals:
void mentioned(QString username);
public slots:
void hideStart();
void hideFinish();
void showStart();
private:
void recount(bool toDown = false);
QPixmap _cache;
MentionRows _rows;
ScrollArea _scroll;
MentionsInner _inner;
ChatData *_chat;
QString _filter;
QRect _boundings;
int32 _width, _height;
bool _hiding;
anim::fvalue a_opacity;
QTimer _hideTimer;
BoxShadow _shadow;
};
//class StickerPanInner : public QWidget, public Animated { //class StickerPanInner : public QWidget, public Animated {
// Q_OBJECT // Q_OBJECT
// //

View file

@ -180,6 +180,79 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
return 0; return 0;
} }
bool FlatTextarea::getMentionStart(QString &start) const {
int32 pos = textCursor().position();
if (textCursor().anchor() != pos) return false;
QTextDocument *doc(document());
QTextBlock block = doc->findBlock(pos);
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) {
QTextFragment fr(iter.fragment());
if (!fr.isValid()) continue;
int32 p = fr.position(), e = (p + fr.length());
if (p >= pos || e < pos) continue;
QTextCharFormat f = fr.charFormat();
if (f.isImageFormat()) continue;
QString t(fr.text());
for (int i = pos - p; i > 0; --i) {
if (t.at(i - 1) == '@') {
start = t.mid(i, pos - p - i);
return (start.isEmpty() || start.at(0).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'));
}
if (pos - p - i > 31) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
}
return false;
}
return false;
}
void FlatTextarea::onMentionInsert(QString mention) {
QTextCursor c(textCursor());
int32 pos = c.position();
QTextDocument *doc(document());
QTextBlock block = doc->findBlock(pos);
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) {
QTextFragment fr(iter.fragment());
if (!fr.isValid()) continue;
int32 p = fr.position(), e = (p + fr.length());
if (p >= pos || e < pos) continue;
QTextCharFormat f = fr.charFormat();
if (f.isImageFormat()) continue;
QString t(fr.text());
for (int i = pos - p; i > 0; --i) {
if (t.at(i - 1) == '@') {
if ((i == pos - p || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
c.setPosition(p + i, QTextCursor::MoveAnchor);
int till = p + i;
for (; (till < e) && (till - p - i < mention.size()); ++till) {
if (t.at(till - p).toLower() != mention.at(till - p - i).toLower()) {
break;
}
}
if (till - p - i == mention.size() && till < e && t.at(till - p) == ' ') {
++till;
}
c.setPosition(till, QTextCursor::KeepAnchor);
c.insertText(mention + ' ');
return;
}
break;
}
if (pos - p - i > 31) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
}
}
c.insertText('@' + mention + ' ');
}
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const { void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
int32 end = textCursor().position(), start = end - 1; int32 end = textCursor().position(), start = end - 1;
if (textCursor().anchor() != end) return; if (textCursor().anchor() != end) return;

View file

@ -49,6 +49,7 @@ public:
QSize minimumSizeHint() const; QSize minimumSizeHint() const;
EmojiPtr getSingleEmoji() const; EmojiPtr getSingleEmoji() const;
bool getMentionStart(QString &start) const;
void removeSingleEmoji(); void removeSingleEmoji();
QString getText(int32 start = 0, int32 end = -1) const; QString getText(int32 start = 0, int32 end = -1) const;
bool hasText() const; bool hasText() const;
@ -66,6 +67,8 @@ public slots:
void onUndoAvailable(bool avail); void onUndoAvailable(bool avail);
void onRedoAvailable(bool avail); void onRedoAvailable(bool avail);
void onMentionInsert(QString mention);
signals: signals:
void changed(); void changed();

View file

@ -137,6 +137,7 @@ namespace {
const QRegularExpression reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$")); const QRegularExpression reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@")); const QRegularExpression reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
const QRegularExpression reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[A-Za-z_\\.0-9]{2,20}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)")); const QRegularExpression reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[A-Za-z_\\.0-9]{2,20}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"));
const QRegularExpression reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"));
QSet<int32> validProtocols, validTopDomains; QSet<int32> validProtocols, validTopDomains;
void initLinkSets(); void initLinkSets();
@ -418,7 +419,10 @@ public:
} }
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) { void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (!original.isEmpty() && original.at(0) == '#') { if (!original.isEmpty() && original.at(0) == '@') {
result = original;
fullDisplayed = -3; // mention
} else if (!original.isEmpty() && original.at(0) == '#') {
result = original; result = original;
fullDisplayed = -2; // hashtag fullDisplayed = -2; // hashtag
} else if (reMailStart.match(original).hasMatch()) { } else if (reMailStart.match(original).hasMatch()) {
@ -685,7 +689,9 @@ public:
_t->_links.resize(lnkIndex); _t->_links.resize(lnkIndex);
const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]); const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]);
TextLinkPtr lnk; TextLinkPtr lnk;
if (data.fullDisplayed < -1) { // hashtag if (data.fullDisplayed < -2) { // mention
lnk = TextLinkPtr(new MentionLink(data.url));
} else if (data.fullDisplayed < -1) { // hashtag
lnk = TextLinkPtr(new HashtagLink(data.url)); lnk = TextLinkPtr(new HashtagLink(data.url));
} else if (data.fullDisplayed < 0) { // email } else if (data.fullDisplayed < 0) { // email
lnk = TextLinkPtr(new EmailLink(data.url)); lnk = TextLinkPtr(new EmailLink(data.url));
@ -716,7 +722,7 @@ private:
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) { TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
} }
QString url; QString url;
int32 fullDisplayed; // -2 - hashtag, -1 - email int32 fullDisplayed; // -3 - mention, -2 - hashtag, -1 - email
}; };
typedef QVector<TextLinkData> TextLinks; typedef QVector<TextLinkData> TextLinks;
TextLinks links; TextLinks links;
@ -849,6 +855,12 @@ void TextLink::onClick(Qt::MouseButton button) const {
} }
} }
void MentionLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::openUserByName(_tag.mid(1), true);
}
}
void HashtagLink::onClick(Qt::MouseButton button) const { void HashtagLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) { if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::searchByHashtag(_tag); App::searchByHashtag(_tag);
@ -4137,7 +4149,8 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
QRegularExpressionMatch mDomain = reDomain.match(text, matchOffset); QRegularExpressionMatch mDomain = reDomain.match(text, matchOffset);
QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(text, matchOffset); QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(text, matchOffset);
QRegularExpressionMatch mHashtag = reHashtag.match(text, matchOffset); QRegularExpressionMatch mHashtag = reHashtag.match(text, matchOffset);
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break; QRegularExpressionMatch mMention = reMention.match(text, matchOffset);
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch()) break;
LinkRange link; LinkRange link;
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX, int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
@ -4145,7 +4158,9 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX, explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX,
explicitDomainEnd = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedEnd() : INT_MAX, explicitDomainEnd = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedEnd() : INT_MAX,
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX, hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX; hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX,
mentionOffset = mMention.hasMatch() ? mMention.capturedStart() : INT_MAX,
mentionEnd = mMention.hasMatch() ? mMention.capturedEnd() : INT_MAX;
if (mHashtag.hasMatch()) { if (mHashtag.hasMatch()) {
if (!mHashtag.capturedRef(1).isEmpty()) { if (!mHashtag.capturedRef(1).isEmpty()) {
++hashtagOffset; ++hashtagOffset;
@ -4154,12 +4169,35 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
--hashtagEnd; --hashtagEnd;
} }
} }
if (mMention.hasMatch()) {
if (!mMention.capturedRef(1).isEmpty()) {
++mentionOffset;
}
if (!mMention.capturedRef(2).isEmpty()) {
--mentionEnd;
}
if (!(start + mentionOffset + 1)->isLetter() || !(start + mentionEnd - 1)->isLetterOrNumber()) {
mentionOffset = mentionEnd = INT_MAX;
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
}
}
if (explicitDomainOffset < domainOffset) { if (explicitDomainOffset < domainOffset) {
domainOffset = explicitDomainOffset; domainOffset = explicitDomainOffset;
domainEnd = explicitDomainEnd; domainEnd = explicitDomainEnd;
mDomain = mExplicitDomain; mDomain = mExplicitDomain;
} }
if (hashtagOffset < domainOffset) { if (mentionOffset < hashtagOffset && mentionOffset < domainOffset) {
if (mentionOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && mentionOffset < (after - start)) {
nextCmd = offset = matchOffset = after - start;
continue;
}
}
link.from = start + mentionOffset;
link.len = start + mentionEnd - link.from;
} else if (hashtagOffset < domainOffset) {
if (hashtagOffset > nextCmd) { if (hashtagOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len); const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && hashtagOffset < (after - start)) { if (after > start + nextCmd && hashtagOffset < (after - start)) {

View file

@ -301,6 +301,32 @@ private:
}; };
class MentionLink : public ITextLink {
public:
MentionLink(const QString &tag) : _tag(tag) {
}
const QString &text() const {
return _tag;
}
void onClick(Qt::MouseButton button) const;
const QString &readable() const {
return _tag;
}
QString encoded() const {
return _tag;
}
private:
QString _tag;
};
class HashtagLink : public ITextLink { class HashtagLink : public ITextLink {
public: public:

View file

@ -25,10 +25,10 @@ public:
TWidget(QWidget *parent = 0) : QWidget(parent) { TWidget(QWidget *parent = 0) : QWidget(parent) {
} }
TWidget *tparent() { TWidget *tparent() {
return dynamic_cast<TWidget*>(parentWidget()); return qobject_cast<TWidget*>(parentWidget());
} }
const TWidget *tparent() const { const TWidget *tparent() const {
return dynamic_cast<const TWidget*>(parentWidget()); return qobject_cast<const TWidget*>(parentWidget());
} }
virtual void leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget virtual void leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget

File diff suppressed because it is too large Load diff

View file

@ -164,6 +164,8 @@ struct ChatData : public PeerData {
Participants participants; Participants participants;
typedef QMap<UserData*, bool> CanKick; typedef QMap<UserData*, bool> CanKick;
CanKick cankick; CanKick cankick;
typedef QList<UserData*> LastAuthors;
LastAuthors lastAuthors;
ImagePtr photoFull; ImagePtr photoFull;
PhotoId photoId; PhotoId photoId;
// geo // geo
@ -176,6 +178,7 @@ struct PhotoData {
} }
void forget() { void forget() {
thumb->forget(); thumb->forget();
replyPreview->forget();
medium->forget(); medium->forget();
full->forget(); full->forget();
} }
@ -183,7 +186,7 @@ struct PhotoData {
uint64 access; uint64 access;
int32 user; int32 user;
int32 date; int32 date;
ImagePtr thumb; ImagePtr thumb, replyPreview;
ImagePtr medium; ImagePtr medium;
ImagePtr full; ImagePtr full;
ChatData *chat; // for chat photos connection ChatData *chat; // for chat photos connection
@ -223,6 +226,7 @@ struct VideoData {
void forget() { void forget() {
thumb->forget(); thumb->forget();
replyPreview->forget();
} }
void save(const QString &toFile); void save(const QString &toFile);
@ -258,7 +262,7 @@ struct VideoData {
int32 date; int32 date;
int32 duration; int32 duration;
int32 w, h; int32 w, h;
ImagePtr thumb; ImagePtr thumb, replyPreview;
int32 dc, size; int32 dc, size;
// geo, caption // geo, caption
@ -406,6 +410,7 @@ struct DocumentData {
void forget() { void forget() {
thumb->forget(); thumb->forget();
sticker->forget(); sticker->forget();
replyPreview->forget();
} }
void save(const QString &toFile); void save(const QString &toFile);
@ -443,7 +448,7 @@ struct DocumentData {
uint64 access; uint64 access;
int32 date; int32 date;
QString name, mime, alt; // alt - for stickers QString name, mime, alt; // alt - for stickers
ImagePtr thumb; ImagePtr thumb, replyPreview;
int32 dc; int32 dc;
int32 size; int32 size;
@ -639,13 +644,13 @@ struct History : public QList<HistoryBlock*> {
HistoryItem *createItem(HistoryBlock *block, const MTPmessage &msg, bool newMsg, bool returnExisting = false); HistoryItem *createItem(HistoryBlock *block, const MTPmessage &msg, bool newMsg, bool returnExisting = false);
HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, HistoryMessage *msg); HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, HistoryMessage *msg);
HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc); HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
HistoryItem *addToBackService(MsgId msgId, QDateTime date, const QString &text, bool out = false, bool unread = false, HistoryMedia *media = 0, bool newMsg = true); HistoryItem *addToBackService(MsgId msgId, QDateTime date, const QString &text, int32 flags = 0, HistoryMedia *media = 0, bool newMsg = true);
HistoryItem *addToBack(const MTPmessage &msg, bool newMsg = true); HistoryItem *addToBack(const MTPmessage &msg, bool newMsg = true);
HistoryItem *addToHistory(const MTPmessage &msg); HistoryItem *addToHistory(const MTPmessage &msg);
HistoryItem *addToBackForwarded(MsgId id, HistoryMessage *item); HistoryItem *addToBackForwarded(MsgId id, HistoryMessage *item);
HistoryItem *addToBackDocument(MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc); HistoryItem *addToBackDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
void addToFront(const QVector<MTPMessage> &slice); void addToFront(const QVector<MTPMessage> &slice);
void addToBack(const QVector<MTPMessage> &slice); void addToBack(const QVector<MTPMessage> &slice);
@ -711,6 +716,9 @@ struct History : public QList<HistoryBlock*> {
notifies.pop_front(); notifies.pop_front();
} }
} }
void popNotification(HistoryItem *item) {
if (!notifies.isEmpty() && notifies.back() == item) notifies.pop_back();
}
void itemReplaced(HistoryItem *old, HistoryItem *item) { void itemReplaced(HistoryItem *old, HistoryItem *item) {
if (!notifies.isEmpty()) { if (!notifies.isEmpty()) {
@ -728,6 +736,7 @@ struct History : public QList<HistoryBlock*> {
} }
QString draft; QString draft;
MsgId draftToId;
MessageCursor draftCursor; MessageCursor draftCursor;
int32 lastWidth, lastScrollTop; int32 lastWidth, lastScrollTop;
bool mute; bool mute;
@ -1099,11 +1108,13 @@ private:
ItemAnimations &itemAnimations(); ItemAnimations &itemAnimations();
class HistoryReply; // dynamic_cast optimize
class HistoryMedia; class HistoryMedia;
class HistoryItem : public HistoryElem { class HistoryItem : public HistoryElem {
public: public:
HistoryItem(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime msgDate, int32 from); HistoryItem(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime msgDate, int32 from);
enum { enum {
MsgType = 0, MsgType = 0,
@ -1118,10 +1129,7 @@ public:
const History *history() const { const History *history() const {
return _history; return _history;
} }
UserData *from() { UserData *from() const {
return _from;
}
const UserData *from() const {
return _from; return _from;
} }
HistoryBlock *block() { HistoryBlock *block() {
@ -1137,11 +1145,14 @@ public:
return !_block; return !_block;
} }
bool out() const { bool out() const {
return _out; return _flags & MTPDmessage_flag_out;
} }
bool unread() const { bool unread() const {
if ((_out && (id > 0 && id <= _history->outboxReadTill)) || (!_out && id > 0 && id <= _history->inboxReadTill)) return false; if ((out() && (id > 0 && id <= _history->outboxReadTill)) || (!out() && id > 0 && id <= _history->inboxReadTill)) return false;
return _unread; return _flags & MTPDmessage_flag_unread;
}
bool notifyByFrom() const {
return _flags & MTPDmessage_flag_notify_by_from;
} }
virtual bool needCheck() const { virtual bool needCheck() const {
return true; return true;
@ -1175,6 +1186,12 @@ public:
virtual QString selectedText(uint32 selection) const { virtual QString selectedText(uint32 selection) const {
return qsl("[-]"); return qsl("[-]");
} }
virtual QString inDialogsText() const {
return qsl("-");
}
virtual QString inReplyText() const {
return inDialogsText();
}
virtual void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const = 0; virtual void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const = 0;
virtual QString notificationHeader() const { virtual QString notificationHeader() const {
@ -1199,6 +1216,13 @@ public:
return false; return false;
} }
virtual HistoryReply *toHistoryReply() { // dynamic_cast optimize
return 0;
}
virtual const HistoryReply *toHistoryReply() const { // dynamic_cast optimize
return 0;
}
virtual ~HistoryItem(); virtual ~HistoryItem();
protected: protected:
@ -1207,10 +1231,27 @@ protected:
mutable int32 _fromVersion; mutable int32 _fromVersion;
History *_history; History *_history;
HistoryBlock *_block; HistoryBlock *_block;
bool _out, _unread; int32 _flags;
}; };
class MessageLink : public ITextLink {
public:
MessageLink(PeerId peer, MsgId msgid) : _peer(peer), _msgid(msgid) {
}
void onClick(Qt::MouseButton button) const;
PeerId peer() const {
return _peer;
}
MsgId msgid() const {
return _msgid;
}
private:
PeerId _peer;
MsgId _msgid;
};
HistoryItem *regItem(HistoryItem *item, bool returnExisting = false); HistoryItem *regItem(HistoryItem *item, bool returnExisting = false);
class HistoryMedia : public HistoryElem { class HistoryMedia : public HistoryElem {
@ -1254,8 +1295,15 @@ public:
return false; return false;
} }
virtual bool hasReplyPreview() const {
return false;
}
virtual ImagePtr replyPreview() {
return ImagePtr();
}
int32 currentWidth() const { int32 currentWidth() const {
return w; return qMin(w, _maxw);
} }
protected: protected:
@ -1299,6 +1347,11 @@ public:
return data->full->loading() ? true : !data->medium->loaded(); return data->full->loading() ? true : !data->medium->loaded();
} }
bool hasReplyPreview() const {
return !data->thumb->isNull();
}
ImagePtr replyPreview();
private: private:
int16 pixw, pixh; int16 pixw, pixh;
PhotoData *data; PhotoData *data;
@ -1330,6 +1383,11 @@ public:
void regItem(HistoryItem *item); void regItem(HistoryItem *item);
void unregItem(HistoryItem *item); void unregItem(HistoryItem *item);
bool hasReplyPreview() const {
return !data->thumb->isNull();
}
ImagePtr replyPreview();
private: private:
VideoData *data; VideoData *data;
TextLinkPtr _openl, _savel, _cancell; TextLinkPtr _openl, _savel, _cancell;
@ -1403,6 +1461,11 @@ public:
void updateFrom(const MTPMessageMedia &media); void updateFrom(const MTPMessageMedia &media);
bool hasReplyPreview() const {
return !data->thumb->isNull();
}
ImagePtr replyPreview();
private: private:
DocumentData *data; DocumentData *data;
@ -1449,6 +1512,7 @@ private:
int16 pixw, pixh; int16 pixw, pixh;
DocumentData *data; DocumentData *data;
QString _emoji; QString _emoji;
int32 lastw;
}; };
@ -1459,7 +1523,6 @@ public:
void initDimensions(const HistoryItem *parent); void initDimensions(const HistoryItem *parent);
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const; void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const;
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
HistoryMediaType type() const { HistoryMediaType type() const {
return MediaTypeContact; return MediaTypeContact;
} }
@ -1560,9 +1623,9 @@ class HistoryMessage : public HistoryItem {
public: public:
HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg); HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg);
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media); HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media);
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, HistoryMedia *media); HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc); HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc);
void initMedia(const MTPMessageMedia &media, QString &currentText); void initMedia(const MTPMessageMedia &media, QString &currentText);
void initMediaFromDocument(DocumentData *doc); void initMediaFromDocument(DocumentData *doc);
@ -1593,6 +1656,7 @@ public:
void updateStickerEmoji(); void updateStickerEmoji();
QString selectedText(uint32 selection) const; QString selectedText(uint32 selection) const;
QString inDialogsText() const;
HistoryMedia *getMedia(bool inOverview = false) const; HistoryMedia *getMedia(bool inOverview = false) const;
QString time() const { QString time() const {
@ -1605,6 +1669,13 @@ public:
return _media ? _media->animating() : false; return _media ? _media->animating() : false;
} }
virtual QDateTime dateForwarded() const { // dynamic_cast optimize
return date;
}
virtual UserData *fromForwarded() const { // dynamic_cast optimize
return from();
}
~HistoryMessage(); ~HistoryMessage();
protected: protected:
@ -1625,6 +1696,7 @@ public:
HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg); HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg);
HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg); HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg);
void initDimensions(const HistoryItem *parent = 0);
void fwdNameUpdated() const; void fwdNameUpdated() const;
void draw(QPainter &p, uint32 selection) const; void draw(QPainter &p, uint32 selection) const;
@ -1652,11 +1724,62 @@ protected:
}; };
class HistoryReply : public HistoryMessage {
public:
HistoryReply(History *history, HistoryBlock *block, const MTPDmessage &msg);
HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
void initDimensions(const HistoryItem *parent = 0);
bool updateReplyTo(bool force = false);
void replyToNameUpdated() const;
int32 replyToWidth() const;
TextLinkPtr replyToLink() const;
MsgId replyToId() const;
HistoryItem *replyToMessage() const;
void replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void draw(QPainter &p, uint32 selection) const;
void drawReplyTo(QPainter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const;
void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
UserData *replyTo() const {
return replyToMsg ? replyToMsg->from() : 0;
}
QString selectedText(uint32 selection) const;
HistoryReply *toHistoryReply() { // dynamic_cast optimize
return this;
}
const HistoryReply *toHistoryReply() const { // dynamic_cast optimize
return this;
}
~HistoryReply();
protected:
MsgId replyToMsgId;
HistoryItem *replyToMsg;
TextLinkPtr replyToLnk;
mutable Text replyToName, replyToText;
mutable int32 replyToVersion;
mutable int32 _maxReplyWidth;
int32 toWidth;
};
class HistoryServiceMsg : public HistoryItem { class HistoryServiceMsg : public HistoryItem {
public: public:
HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg); HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg);
HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, bool out = false, bool unread = false, HistoryMedia *media = 0); HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, int32 flags = 0, HistoryMedia *media = 0);
void initDimensions(const HistoryItem *parent = 0); void initDimensions(const HistoryItem *parent = 0);
@ -1679,6 +1802,8 @@ public:
return true; return true;
} }
QString selectedText(uint32 selection) const; QString selectedText(uint32 selection) const;
QString inDialogsText() const;
QString inReplyText() const;
HistoryMedia *getMedia(bool inOverview = false) const; HistoryMedia *getMedia(bool inOverview = false) const;

View file

@ -638,7 +638,8 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
} }
_contextMenuLnk = textlnkOver(); _contextMenuLnk = textlnkOver();
PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data());
VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data()); VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data());
AudioLink *lnkAudio = dynamic_cast<AudioLink*>(_contextMenuLnk.data()); AudioLink *lnkAudio = dynamic_cast<AudioLink*>(_contextMenuLnk.data());
DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data());
@ -647,6 +648,9 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (isUponSelected > 0) { if (isUponSelected > 0) {
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
} }
if (item && item->id > 0 && isUponSelected != 2 && isUponSelected != -2) {
_menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage()));
}
if (lnkPhoto) { if (lnkPhoto) {
_menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true); _menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true);
_menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true); _menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true);
@ -679,7 +683,6 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
App::contextItem(App::hoveredLinkItem()); App::contextItem(App::hoveredLinkItem());
} }
} else { // maybe cursor on some text history item? } else { // maybe cursor on some text history item?
HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
bool canDelete = (item && item->itemType() == HistoryItem::MsgType); bool canDelete = (item && item->itemType() == HistoryItem::MsgType);
bool canForward = canDelete && (item->id > 0) && !item->serviceMsg(); bool canForward = canDelete && (item->id > 0) && !item->serviceMsg();
@ -689,11 +692,20 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (isUponSelected > 0) { if (isUponSelected > 0) {
if (!_menu) _menu = new ContextMenu(this); if (!_menu) _menu = new ContextMenu(this);
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
} else if (item && !isUponSelected && !_contextMenuLnk) { if (item && item->id > 0 && isUponSelected != 2) {
QString contextMenuText = item->selectedText(FullItemSel); _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage()));
if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || msg->getMedia()->type() != MediaTypeSticker)) { }
} else {
if (item && item->id > 0 && isUponSelected != -2) {
if (!_menu) _menu = new ContextMenu(this); if (!_menu) _menu = new ContextMenu(this);
_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true); _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage()));
}
if (item && !isUponSelected && !_contextMenuLnk) {
QString contextMenuText = item->selectedText(FullItemSel);
if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || msg->getMedia()->type() != MediaTypeSticker)) {
if (!_menu) _menu = new ContextMenu(this);
_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true);
}
} }
} }
@ -705,6 +717,10 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (!_menu) _menu = new ContextMenu(historyWidget); if (!_menu) _menu = new ContextMenu(historyWidget);
_menu->addAction(lang(lng_context_open_email), this, SLOT(openContextUrl()))->setEnabled(true); _menu->addAction(lang(lng_context_open_email), this, SLOT(openContextUrl()))->setEnabled(true);
_menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true);
} else if (_contextMenuLnk && dynamic_cast<MentionLink*>(_contextMenuLnk.data())) {
if (!_menu) _menu = new ContextMenu(historyWidget);
_menu->addAction(lng_context_open_mention(lt_user, _contextMenuLnk->encoded()), this, SLOT(openContextUrl()))->setEnabled(true);
_menu->addAction(lang(lng_context_copy_mention), this, SLOT(copyContextUrl()))->setEnabled(true);
} else if (_contextMenuLnk && dynamic_cast<HashtagLink*>(_contextMenuLnk.data())) { } else if (_contextMenuLnk && dynamic_cast<HashtagLink*>(_contextMenuLnk.data())) {
if (!_menu) _menu = new ContextMenu(historyWidget); if (!_menu) _menu = new ContextMenu(historyWidget);
_menu->addAction(lang(lng_context_open_hashtag), this, SLOT(openContextUrl()))->setEnabled(true); _menu->addAction(lang(lng_context_open_hashtag), this, SLOT(openContextUrl()))->setEnabled(true);
@ -1538,8 +1554,13 @@ HistoryHider::~HistoryHider() {
} }
HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent) HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
, _replyToId(0)
, _replyTo(0)
, _replyToNameVersion(0)
, _replyCancel(this, st::replyCancel)
, _lastStickersUpdate(0) , _lastStickersUpdate(0)
, _stickersUpdateRequest(0) , _stickersUpdateRequest(0)
, _loadingMessages(false)
, histRequestsCount(0) , histRequestsCount(0)
, histPeer(0) , histPeer(0)
, _activeHist(0) , _activeHist(0)
@ -1551,6 +1572,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
, hist(0) , hist(0)
, _histInited(false) , _histInited(false)
, _toHistoryEnd(this, st::historyToEnd) , _toHistoryEnd(this, st::historyToEnd)
, _attachMention(this)
, _send(this, lang(lng_send_button), st::btnSend) , _send(this, lang(lng_send_button), st::btnSend)
, _attachDocument(this, st::btnAttachDocument) , _attachDocument(this, st::btnAttachDocument)
, _attachPhoto(this, st::btnAttachPhoto) , _attachPhoto(this, st::btnAttachPhoto)
@ -1564,8 +1586,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
, _attachDragPhoto(this) , _attachDragPhoto(this)
, imageLoader(this) , imageLoader(this)
, _synthedTextUpdate(false) , _synthedTextUpdate(false)
, loadingChatId(0)
, loadingRequestId(0)
, serviceImageCacheSize(0) , serviceImageCacheSize(0)
, confirmImageId(0) , confirmImageId(0)
, confirmWithText(false) , confirmWithText(false)
@ -1581,6 +1601,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll())); connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
connect(&_toHistoryEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); connect(&_toHistoryEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd()));
connect(&_replyCancel, SIGNAL(clicked()), this, SLOT(onReplyCancel()));
connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_send, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
@ -1609,7 +1630,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
_saveDraftTimer.setSingleShot(true); _saveDraftTimer.setSingleShot(true);
connect(&_saveDraftTimer, SIGNAL(timeout()), this, SLOT(onDraftSave())); connect(&_saveDraftTimer, SIGNAL(timeout()), this, SLOT(onDraftSave()));
connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed())); connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed()));
connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed())); connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onFieldCursorChanged()));
_replyCancel.hide();
_scroll.hide(); _scroll.hide();
_scroll.move(0, 0); _scroll.move(0, 0);
@ -1618,6 +1641,10 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
_toHistoryEnd.hide(); _toHistoryEnd.hide();
_attachMention.hide();
connect(&_attachMention, SIGNAL(mentioned(QString)), &_field, SLOT(onMentionInsert(QString)));
_field.installEventFilter(&_attachMention);
_field.hide(); _field.hide();
_field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _send.height() - 2 * st::sendPadding); _field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _send.height() - 2 * st::sendPadding);
_send.hide(); _send.hide();
@ -1645,6 +1672,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
connect(&_attachDragPhoto, SIGNAL(dropped(QDropEvent*)), this, SLOT(onPhotoDrop(QDropEvent*))); connect(&_attachDragPhoto, SIGNAL(dropped(QDropEvent*)), this, SLOT(onPhotoDrop(QDropEvent*)));
} }
void HistoryWidget::start() {
updateRecentStickers();
connect(App::api(), SIGNAL(fullPeerLoaded(PeerData*)), this, SLOT(onPeerLoaded(PeerData*)));
}
void HistoryWidget::onTextChange() { void HistoryWidget::onTextChange() {
updateTyping(); updateTyping();
// updateStickerPan(); // updateStickerPan();
@ -1678,12 +1710,12 @@ void HistoryWidget::onDraftSave(bool delayed) {
writeDraft(); writeDraft();
} }
void HistoryWidget::writeDraft(const QString *text, const MessageCursor *cursor) { void HistoryWidget::writeDraft(MsgId *replyTo, const QString *text, const MessageCursor *cursor) {
bool save = hist && (_saveDraftStart > 0); bool save = hist && (_saveDraftStart > 0);
_saveDraftStart = 0; _saveDraftStart = 0;
_saveDraftTimer.stop(); _saveDraftTimer.stop();
if (_saveDraftText) { if (_saveDraftText) {
if (save) Local::writeDraft(hist->peer->id, text ? (*text) : _field.getText()); if (save) Local::writeDraft(hist->peer->id, Local::MessageDraft(replyTo ? (*replyTo) : _replyToId, text ? (*text) : _field.getText()));
_saveDraftText = false; _saveDraftText = false;
} }
if (save) Local::writeDraftPositions(hist->peer->id, cursor ? (*cursor) : MessageCursor(_field)); if (save) Local::writeDraftPositions(hist->peer->id, cursor ? (*cursor) : MessageCursor(_field));
@ -1748,26 +1780,6 @@ void HistoryWidget::activate() {
} }
} }
void HistoryWidget::chatLoaded(const MTPmessages_ChatFull &res) {
const MTPDmessages_chatFull &d(res.c_messages_chatFull());
PeerId peerId = App::peerFromChat(d.vfull_chat.c_chatFull().vid);
if (peerId == loadingChatId) {
loadingRequestId = 0;
}
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
App::feedParticipants(d.vfull_chat.c_chatFull().vparticipants);
PhotoData *photo = App::feedPhoto(d.vfull_chat.c_chatFull().vchat_photo);
if (photo) {
ChatData *chat = App::peer(peerId)->asChat();
if (chat) {
chat->photoId = photo->id;
photo->chat = chat;
}
}
peerUpdated(App::chat(peerId));
}
void HistoryWidget::updateStickers() { void HistoryWidget::updateStickers() {
if (_lastStickersUpdate && getms(true) < _lastStickersUpdate + StickersUpdateTimeout) return; if (_lastStickersUpdate && getms(true) < _lastStickersUpdate + StickersUpdateTimeout) return;
if (_stickersUpdateRequest) return; if (_stickersUpdateRequest) return;
@ -1949,8 +1961,9 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
if (hist) { if (hist) {
hist->draft = _field.getText(); hist->draft = _field.getText();
hist->draftCursor.fillFrom(_field); hist->draftCursor.fillFrom(_field);
hist->draftToId = _replyToId;
writeDraft(&hist->draft, &hist->draftCursor); writeDraft(&hist->draftToId, &hist->draft, &hist->draftCursor);
if (hist->readyForWork() && _scroll.scrollTop() + 1 <= _scroll.scrollTopMax()) { if (hist->readyForWork() && _scroll.scrollTop() + 1 <= _scroll.scrollTopMax()) {
hist->lastWidth = _list->width(); hist->lastWidth = _list->width();
@ -1961,6 +1974,11 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
if (hist->unreadBar) hist->unreadBar->destroy(); if (hist->unreadBar) hist->unreadBar->destroy();
} }
if (_replyToId) {
_replyTo = 0;
_replyToId = 0;
_replyCancel.hide();
}
_scroll.setWidget(0); _scroll.setWidget(0);
if (_list) _list->deleteLater(); if (_list) _list->deleteLater();
_list = 0; _list = 0;
@ -2029,18 +2047,25 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
App::main()->peerUpdated(histPeer); App::main()->peerUpdated(histPeer);
if (!hist->draft.isEmpty()) { if (hist->draftToId > 0 || !hist->draft.isEmpty()) {
setFieldText(hist->draft); setFieldText(hist->draft);
_field.setFocus(); _field.setFocus();
hist->draftCursor.applyTo(_field, &_synthedTextUpdate); hist->draftCursor.applyTo(_field, &_synthedTextUpdate);
_replyToId = hist->draftToId;
} else { } else {
QString draft = Local::readDraft(hist->peer->id); Local::MessageDraft draft = Local::readDraft(hist->peer->id);
setFieldText(draft); setFieldText(draft.text);
_field.setFocus(); _field.setFocus();
if (!draft.isEmpty()) { if (!draft.text.isEmpty()) {
MessageCursor cur = Local::readDraftPositions(hist->peer->id); MessageCursor cur = Local::readDraftPositions(hist->peer->id);
cur.applyTo(_field, &_synthedTextUpdate); cur.applyTo(_field, &_synthedTextUpdate);
} }
_replyToId = draft.replyTo;
}
if (_replyToId) {
updateReplyTo();
if (!_replyTo) App::api()->requestReplyTo(0, _replyToId);
resizeEvent(0);
} }
connect(&_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged())); connect(&_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged()));
@ -2082,7 +2107,9 @@ void HistoryWidget::updateControlsVisibility() {
_scroll.hide(); _scroll.hide();
_send.hide(); _send.hide();
_toHistoryEnd.hide(); _toHistoryEnd.hide();
_attachMention.hide();
_field.hide(); _field.hide();
_replyCancel.hide();
_attachDocument.hide(); _attachDocument.hide();
_attachPhoto.hide(); _attachPhoto.hide();
_attachEmoji.hide(); _attachEmoji.hide();
@ -2108,10 +2135,12 @@ void HistoryWidget::updateControlsVisibility() {
_attachEmoji.show(); _attachEmoji.show();
if (_field.isHidden()) { if (_field.isHidden()) {
_field.show(); _field.show();
if (_replyToId) _replyCancel.show();
resizeEvent(0); resizeEvent(0);
update(); update();
} }
} else { } else {
_attachMention.hide();
_send.hide(); _send.hide();
_attachDocument.hide(); _attachDocument.hide();
_attachPhoto.hide(); _attachPhoto.hide();
@ -2133,6 +2162,7 @@ void HistoryWidget::updateControlsVisibility() {
loadMessages(); loadMessages();
if (!hist->readyForWork()) { if (!hist->readyForWork()) {
_scroll.hide(); _scroll.hide();
_attachMention.hide();
_send.hide(); _send.hide();
_attachDocument.hide(); _attachDocument.hide();
_attachPhoto.hide(); _attachPhoto.hide();
@ -2141,6 +2171,7 @@ void HistoryWidget::updateControlsVisibility() {
_emojiPan.hide(); _emojiPan.hide();
// _stickerPan.hide(); // _stickerPan.hide();
_toHistoryEnd.hide(); _toHistoryEnd.hide();
_replyCancel.hide();
if (!_field.isHidden()) { if (!_field.isHidden()) {
_field.hide(); _field.hide();
update(); update();
@ -2149,7 +2180,7 @@ void HistoryWidget::updateControlsVisibility() {
} }
} }
void HistoryWidget::newUnreadMsg(History *history, MsgId msgId) { void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) {
if (App::wnd()->historyIsActive()) { if (App::wnd()->historyIsActive()) {
if (hist == history && hist->readyForWork()) { if (hist == history && hist->readyForWork()) {
historyWasRead(); historyWasRead();
@ -2158,7 +2189,7 @@ void HistoryWidget::newUnreadMsg(History *history, MsgId msgId) {
} }
} else { } else {
if (hist != history) { if (hist != history) {
App::wnd()->notifySchedule(history, msgId); App::wnd()->notifySchedule(history, item);
} }
history->setUnreadCount(history->unreadCount + 1); history->setUnreadCount(history->unreadCount + 1);
} }
@ -2168,7 +2199,7 @@ void HistoryWidget::newUnreadMsg(History *history, MsgId msgId) {
if (history->unreadBar) history->unreadBar->destroy(); if (history->unreadBar) history->unreadBar->destroy();
} }
} }
App::wnd()->notifySchedule(history, msgId); App::wnd()->notifySchedule(history, item);
history->setUnreadCount(history->unreadCount + 1); history->setUnreadCount(history->unreadCount + 1);
history->lastWidth = 0; history->lastWidth = 0;
} }
@ -2324,7 +2355,7 @@ bool HistoryWidget::isActive() const {
} }
void HistoryWidget::loadMessages() { void HistoryWidget::loadMessages() {
if (!hist) return; if (!hist || _loadingMessages) return;
if (hist->loadedAtTop()) { if (hist->loadedAtTop()) {
if (!hist->readyForWork()) { if (!hist->readyForWork()) {
if (hist->activeMsgId) { if (hist->activeMsgId) {
@ -2338,13 +2369,14 @@ void HistoryWidget::loadMessages() {
return; return;
} }
int32 dh = 0; _loadingMessages = true;
if (histPreload.size()) { if (histPreload.size()) {
bool loaded = hist->readyForWork(); bool loaded = hist->readyForWork();
addMessagesToFront(histPreload); addMessagesToFront(histPreload);
histPreload.clear(); histPreload.clear();
checkUnreadLoaded(true); checkUnreadLoaded(true);
if (!loaded && hist->readyForWork()) { if (!loaded && hist->readyForWork()) {
_loadingMessages = false;
return; return;
} }
} }
@ -2361,6 +2393,7 @@ void HistoryWidget::loadMessages() {
} else { } else {
checkUnreadLoaded(true); checkUnreadLoaded(true);
} }
_loadingMessages = false;
} }
void HistoryWidget::loadMessagesDown() { void HistoryWidget::loadMessagesDown() {
@ -2443,7 +2476,7 @@ void HistoryWidget::onHistoryToEnd() {
} }
} }
void HistoryWidget::onSend(bool ctrlShiftEnter) { void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
if (!hist) return; if (!hist) return;
QString text = prepareMessage(_field.getText()); QString text = prepareMessage(_field.getText());
@ -2451,18 +2484,19 @@ void HistoryWidget::onSend(bool ctrlShiftEnter) {
App::main()->readServerHistory(hist, false); App::main()->readServerHistory(hist, false);
hist->loadAround(0); hist->loadAround(0);
App::main()->sendPreparedText(hist, prepareMessage(_field.getText())); App::main()->sendPreparedText(hist, prepareMessage(_field.getText()), replyTo);
setFieldText(QString()); setFieldText(QString());
_saveDraftText = true; _saveDraftText = true;
_saveDraftStart = getms(); _saveDraftStart = getms();
onDraftSave(); onDraftSave();
if (!_attachMention.isHidden()) _attachMention.hideStart();
if (!_attachType.isHidden()) _attachType.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart(); if (!_emojiPan.isHidden()) _emojiPan.hideStart();
// if (!_stickerPan.isHidden()) _stickerPan.hideStart(); // if (!_stickerPan.isHidden()) _stickerPan.hideStart();
} }
if (replyTo < 0) onReplyCancel();
_field.setFocus(); _field.setFocus();
} }
@ -2539,10 +2573,11 @@ void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
App::main()->showPeer(peer, 0, false, true); App::main()->showPeer(peer, 0, false, true);
if (!hist) return; if (!hist) return;
shareContact(peer, contact->phone, contact->firstName, contact->lastName, int32(contact->id & 0xFFFFFFFF)); shareContact(peer, contact->phone, contact->firstName, contact->lastName, _replyToId, int32(contact->id & 0xFFFFFFFF));
onReplyCancel();
} }
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, int32 userId) { void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId) {
History *h = App::history(peer); History *h = App::history(peer);
App::main()->readServerHistory(h, false); App::main()->readServerHistory(h, false);
@ -2553,9 +2588,9 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const
PeerData *p = App::peer(peer); PeerData *p = App::peer(peer);
int32 flags = (p->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out int32 flags = (p->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(peer), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId)))); if (replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(peer), MTPint(), MTPint(), MTP_int(_replyToId), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId))));
h->sendRequestId = MTP::send(MTPmessages_SendMedia(p->input, MTP_int(0), MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); h->sendRequestId = MTP::send(MTPmessages_SendMedia(p->input, MTP_int(replyTo), MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
if (hist && histPeer && peer == histPeer->id) { if (hist && histPeer && peer == histPeer->id) {
@ -2605,6 +2640,7 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
_attachPhoto.hide(); _attachPhoto.hide();
_attachEmoji.hide(); _attachEmoji.hide();
_field.hide(); _field.hide();
_replyCancel.hide();
_send.hide(); _send.hide();
a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0); a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0);
a_alpha = anim::fvalue(0, 1); a_alpha = anim::fvalue(0, 1);
@ -2984,20 +3020,39 @@ void HistoryWidget::updateOnlineDisplayTimer() {
void HistoryWidget::onFieldResize() { void HistoryWidget::onFieldResize() {
_field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding); _field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding);
_replyCancel.move(width() - _replyCancel.width(), _field.y() - st::sendPadding - _replyCancel.height());
updateListSize(); updateListSize();
int backy = _scroll.y() + _scroll.height();
update(0, backy, width(), height() - backy);
} }
void HistoryWidget::onFieldFocused() { void HistoryWidget::onFieldFocused() {
if (_list) _list->clearSelectedItems(true); if (_list) _list->clearSelectedItems(true);
} }
void HistoryWidget::checkMentionDropdown() {
if (!hist || !hist->peer->chat) return;
QString start;
if (_field.getMentionStart(start)) {
_attachMention.showFiltered(hist->peer->asChat(), start);
} else if (!_attachMention.isHidden()) {
_attachMention.hideStart();
}
}
void HistoryWidget::onFieldCursorChanged() {
checkMentionDropdown();
onDraftSaveDelayed();
}
void HistoryWidget::uploadImage(const QImage &img, bool withText) { void HistoryWidget::uploadImage(const QImage &img, bool withText) {
if (!hist || confirmImageId) return; if (!hist || confirmImageId) return;
App::wnd()->activateWindow(); App::wnd()->activateWindow();
confirmImage = img; confirmImage = img;
confirmWithText = withText; confirmWithText = withText;
confirmImageId = imageLoader.append(img, histPeer->id, ToPreparePhoto); confirmImageId = imageLoader.append(img, histPeer->id, _replyToId, ToPreparePhoto);
} }
void HistoryWidget::uploadFile(const QString &file, bool withText) { void HistoryWidget::uploadFile(const QString &file, bool withText) {
@ -3005,44 +3060,47 @@ void HistoryWidget::uploadFile(const QString &file, bool withText) {
App::wnd()->activateWindow(); App::wnd()->activateWindow();
confirmWithText = withText; confirmWithText = withText;
confirmImageId = imageLoader.append(file, histPeer->id, ToPrepareDocument); confirmImageId = imageLoader.append(file, histPeer->id, _replyToId, ToPrepareDocument);
} }
void HistoryWidget::shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText) { void HistoryWidget::shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText) {
if (!hist || confirmImageId) return; if (!hist || confirmImageId) return;
App::wnd()->activateWindow(); App::wnd()->activateWindow();
confirmWithText = withText; confirmWithText = withText;
confirmImageId = 0xFFFFFFFFFFFFFFFFL; confirmImageId = 0xFFFFFFFFFFFFFFFFL;
App::wnd()->showLayer(new PhotoSendBox(phone, fname, lname)); App::wnd()->showLayer(new PhotoSendBox(phone, fname, lname, replyTo));
} }
void HistoryWidget::uploadConfirmImageUncompressed(bool ctrlShiftEnter) { void HistoryWidget::uploadConfirmImageUncompressed(bool ctrlShiftEnter, MsgId replyTo) {
if (!hist || !confirmImageId || confirmImage.isNull()) return; if (!hist || !confirmImageId || confirmImage.isNull()) return;
App::wnd()->activateWindow(); App::wnd()->activateWindow();
PeerId peerId = histPeer->id; PeerId peerId = histPeer->id;
if (confirmWithText) { if (confirmWithText) {
onSend(ctrlShiftEnter); onSend(ctrlShiftEnter, replyTo);
} }
imageLoader.append(confirmImage, peerId, ToPrepareDocument, ctrlShiftEnter); imageLoader.append(confirmImage, peerId, replyTo, ToPrepareDocument, ctrlShiftEnter);
confirmImageId = 0; confirmImageId = 0;
confirmWithText = false; confirmWithText = false;
confirmImage = QImage(); confirmImage = QImage();
onReplyCancel();
} }
void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType type) { void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType type) {
if (!hist) return; if (!hist) return;
App::wnd()->activateWindow(); App::wnd()->activateWindow();
imageLoader.append(files, histPeer->id, type); imageLoader.append(files, histPeer->id, _replyToId, type);
onReplyCancel();
} }
void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer) { void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer) {
if (!peer && !hist) return; if (!peer && !hist) return;
App::wnd()->activateWindow(); App::wnd()->activateWindow();
imageLoader.append(fileContent, peer ? peer : histPeer->id, type); imageLoader.append(fileContent, peer ? peer : histPeer->id, _replyToId, type);
onReplyCancel();
} }
void HistoryWidget::onPhotoReady() { void HistoryWidget::onPhotoReady() {
@ -3062,25 +3120,26 @@ void HistoryWidget::onPhotoReady() {
void HistoryWidget::onPhotoFailed(quint64 id) { void HistoryWidget::onPhotoFailed(quint64 id) {
} }
void HistoryWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname) { void HistoryWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo) {
if (!histPeer) return; if (!histPeer) return;
PeerId peerId = histPeer->id; PeerId peerId = histPeer->id;
if (0xFFFFFFFFFFFFFFFFL == confirmImageId) { if (0xFFFFFFFFFFFFFFFFL == confirmImageId) {
if (confirmWithText) { if (confirmWithText) {
onSend(ctrlShiftEnter); onSend(ctrlShiftEnter, replyTo);
} }
confirmImageId = 0; confirmImageId = 0;
confirmWithText = false; confirmWithText = false;
confirmImage = QImage(); confirmImage = QImage();
} }
shareContact(peerId, phone, fname, lname); shareContact(peerId, phone, fname, lname, replyTo);
onReplyCancel();
} }
void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) { void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
if (img.id == confirmImageId) { if (img.id == confirmImageId) {
if (confirmWithText) { if (confirmWithText) {
onSend(img.ctrlShiftEnter); onSend(img.ctrlShiftEnter, img.replyTo);
} }
confirmImageId = 0; confirmImageId = 0;
confirmWithText = false; confirmWithText = false;
@ -3102,11 +3161,13 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
if (img.type == ToPreparePhoto) { if (img.type == ToPreparePhoto) {
h->loadAround(0); h->loadAround(0);
int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo))); if (img.replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTPint(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo)));
} else if (img.type == ToPrepareDocument) { } else if (img.type == ToPrepareDocument) {
h->loadAround(0); h->loadAround(0);
int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document))); if (img.replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTPint(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document)));
} }
if (hist && histPeer && img.peer == histPeer->id) { if (hist && histPeer && img.peer == histPeer->id) {
@ -3132,7 +3193,8 @@ void HistoryWidget::onPhotoUploaded(MsgId newId, const MTPInputFile &file) {
uint64 randomId = MTP::nonce<uint64>(); uint64 randomId = MTP::nonce<uint64>();
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
History *hist = item->history(); History *hist = item->history();
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_int(0), MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), App::main()->rpcFail(&MainWidget::sendPhotoFailed, randomId), 0, 0, hist->sendRequestId); MsgId replyTo = item->toHistoryReply() ? item->toHistoryReply()->replyToId() : 0;
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), App::main()->rpcFail(&MainWidget::sendPhotoFailed, randomId), 0, 0, hist->sendRequestId);
} }
} }
@ -3167,7 +3229,8 @@ void HistoryWidget::onDocumentUploaded(MsgId newId, const MTPInputFile &file) {
uint64 randomId = MTP::nonce<uint64>(); uint64 randomId = MTP::nonce<uint64>();
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
History *hist = item->history(); History *hist = item->history();
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_int(0), MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); MsgId replyTo = item->toHistoryReply() ? item->toHistoryReply()->replyToId() : 0;
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
} }
} }
} }
@ -3188,7 +3251,8 @@ void HistoryWidget::onThumbDocumentUploaded(MsgId newId, const MTPInputFile &fil
uint64 randomId = MTP::nonce<uint64>(); uint64 randomId = MTP::nonce<uint64>();
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
History *hist = item->history(); History *hist = item->history();
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_int(0), MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); MsgId replyTo = item->toHistoryReply() ? item->toHistoryReply()->replyToId() : 0;
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
} }
} }
} }
@ -3230,7 +3294,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_attachPhoto.move(_attachDocument.x(), _attachDocument.y()); _attachPhoto.move(_attachDocument.x(), _attachDocument.y());
_field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding); _field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding);
_replyCancel.move(width() - _replyCancel.width(), _field.y() - st::sendPadding - _replyCancel.height());
updateListSize(); updateListSize();
_field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _field.height()); _field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _field.height());
@ -3264,10 +3328,14 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
void HistoryWidget::itemRemoved(HistoryItem *item) { void HistoryWidget::itemRemoved(HistoryItem *item) {
if (_list) _list->itemRemoved(item); if (_list) _list->itemRemoved(item);
if (item == _replyTo) {
onReplyCancel();
}
} }
void HistoryWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) { void HistoryWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (_list) _list->itemReplaced(oldItem, newItem); if (_list) _list->itemReplaced(oldItem, newItem);
if (_replyTo == oldItem) _replyTo = newItem;
} }
void HistoryWidget::itemResized(HistoryItem *row) { void HistoryWidget::itemResized(HistoryItem *row) {
@ -3279,6 +3347,10 @@ void HistoryWidget::updateScrollColors() {
_scroll.updateColors(App::historyScrollBarColor(), App::historyScrollBgColor(), App::historyScrollBarOverColor(), App::historyScrollBgOverColor()); _scroll.updateColors(App::historyScrollBarColor(), App::historyScrollBgColor(), App::historyScrollBarOverColor(), App::historyScrollBgOverColor());
} }
MsgId HistoryWidget::replyToId() const {
return _replyToId;
}
void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, HistoryItem *resizedItem) { void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, HistoryItem *resizedItem) {
if (!hist || (!_histInited && !initial)) return; if (!hist || (!_histInited && !initial)) return;
@ -3288,9 +3360,13 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown,
} }
int32 newScrollHeight = height() - (hist->readyForWork() && (!histPeer->chat || !histPeer->asChat()->forbidden) ? (_field.height() + 2 * st::sendPadding) : 0); int32 newScrollHeight = height() - (hist->readyForWork() && (!histPeer->chat || !histPeer->asChat()->forbidden) ? (_field.height() + 2 * st::sendPadding) : 0);
if (_replyToId) {
newScrollHeight -= st::replyHeight;
}
bool wasAtBottom = _scroll.scrollTop() + 1 > _scroll.scrollTopMax(), needResize = _scroll.width() != width() || _scroll.height() != newScrollHeight; bool wasAtBottom = _scroll.scrollTop() + 1 > _scroll.scrollTopMax(), needResize = _scroll.width() != width() || _scroll.height() != newScrollHeight;
if (needResize) { if (needResize) {
_scroll.resize(width(), newScrollHeight); _scroll.resize(width(), newScrollHeight);
_attachMention.setBoundings(_scroll.geometry());
_toHistoryEnd.move((width() - _toHistoryEnd.width()) / 2, _scroll.y() + _scroll.height() - _toHistoryEnd.height() - st::historyToEndSkip); _toHistoryEnd.move((width() - _toHistoryEnd.width()) / 2, _scroll.y() + _scroll.height() - _toHistoryEnd.height() - st::historyToEndSkip);
} }
@ -3441,9 +3517,12 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
hist->loadAround(0); hist->loadAround(0);
bool out = (histPeer->input.type() != mtpc_inputPeerSelf), unread = (histPeer->input.type() != mtpc_inputPeerSelf); bool out = (histPeer->input.type() != mtpc_inputPeerSelf), unread = (histPeer->input.type() != mtpc_inputPeerSelf);
hist->addToBackDocument(newId, out, unread, date(MTP_int(unixtime())), MTP::authedId(), sticker); int32 flags = (histPeer->input.type() != mtpc_inputPeerSelf) ? (MTPDmessage_flag_out | MTPDmessage_flag_unread) : 0;
if (_replyToId) flags |= MTPDmessage::flag_reply_to_msg_id;
hist->addToBackDocument(newId, flags, _replyToId, date(MTP_int(unixtime())), MTP::authedId(), sticker);
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(histPeer->input, MTP_int(0), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); hist->sendRequestId = MTP::send(MTPmessages_SendMedia(histPeer->input, MTP_int(_replyToId), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
onReplyCancel();
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
App::main()->historyToDown(hist); App::main()->historyToDown(hist);
@ -3451,6 +3530,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
App::main()->dialogsToUp(); App::main()->dialogsToUp();
peerMessagesUpdated(histPeer->id); peerMessagesUpdated(histPeer->id);
if (!_attachMention.isHidden()) _attachMention.hideStart();
if (!_attachType.isHidden()) _attachType.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart(); if (!_emojiPan.isHidden()) _emojiPan.hideStart();
// if (!_stickerPan.isHidden()) _stickerPan.hideStart(); // if (!_stickerPan.isHidden()) _stickerPan.hideStart();
@ -3465,18 +3545,56 @@ void HistoryWidget::setFieldText(const QString &text) {
_synthedTextUpdate = false; _synthedTextUpdate = false;
} }
void HistoryWidget::onReplyToMessage() {
HistoryItem *to = App::contextItem();
if (!to || to->id <= 0) return;
_replyTo = to;
_replyToId = to->id;
_replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions);
if (!_field.isHidden()) _replyCancel.show();
updateReplyToName();
resizeEvent(0);
update();
_saveDraftText = true;
_saveDraftStart = getms();
onDraftSave();
_field.setFocus();
}
void HistoryWidget::onReplyCancel() {
if (!_replyToId) return;
_replyTo = 0;
_replyToId = 0;
_replyCancel.hide();
resizeEvent(0);
update();
_saveDraftText = true;
_saveDraftStart = getms();
onDraftSave();
}
void HistoryWidget::onCancel() { void HistoryWidget::onCancel() {
if (App::main()) App::main()->showPeer(0); if (App::main()) App::main()->showPeer(0);
emit cancelled(); emit cancelled();
} }
void HistoryWidget::onPeerLoaded(PeerData *data) {
peerUpdated(data);
if (data == histPeer) {
checkMentionDropdown();
}
}
void HistoryWidget::peerUpdated(PeerData *data) { void HistoryWidget::peerUpdated(PeerData *data) {
if (data && data == histPeer) { if (data && data == histPeer) {
updateListSize(); updateListSize();
if (!animating()) updateControlsVisibility(); if (!animating()) updateControlsVisibility();
if (data->chat && data->asChat()->count > 0 && data->asChat()->participants.isEmpty() && (!loadingRequestId || loadingChatId != data->id)) { if (data->chat && data->asChat()->count > 0 && data->asChat()->participants.isEmpty()) {
loadingChatId = data->id; App::api()->requestFullPeer(data);
loadingRequestId = MTP::send(MTPmessages_GetFullChat(App::peerToMTP(data->id).c_peerChat().vchat_id), rpcDone(&HistoryWidget::chatLoaded));
} }
App::main()->updateOnlineDisplay(); App::main()->updateOnlineDisplay();
} }
@ -3512,7 +3630,7 @@ void HistoryWidget::onDeleteSelectedSure() {
} }
if (!ids.isEmpty()) { if (!ids.isEmpty()) {
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(ids))); App::main()->deleteMessages(ids);
} }
onClearSelected(); onClearSelected();
@ -3532,7 +3650,7 @@ void HistoryWidget::onDeleteContextSure() {
} }
if (item->id > 0) { if (item->id > 0) {
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(1, MTP_int(item->id)))); App::main()->deleteMessages(QVector<MTPint>(1, MTP_int(item->id)));
} }
item->destroy(); item->destroy();
if (App::main() && App::main()->peer() == peer()) { if (App::main() && App::main()->peer() == peer()) {
@ -3588,6 +3706,65 @@ void HistoryWidget::updateTopBarSelection() {
update(); update();
} }
void HistoryWidget::updateReplyTo(bool force) {
if (!_replyToId || _replyTo) return;
_replyTo = App::histItemById(_replyToId);
if (_replyTo) {
_replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions);
if (!_field.isHidden()) _replyCancel.show();
updateReplyToName();
int backy = _scroll.y() + _scroll.height();
update(0, backy, width(), height() - backy);
} else if (force) {
onReplyCancel();
}
}
void HistoryWidget::updateReplyToName() {
if (!_replyTo) return;
_replyToName.setText(st::msgServiceNameFont, App::peerName(_replyTo->from()), _textNameOptions);
_replyToNameVersion = _replyTo->from()->nameVersion;
}
void HistoryWidget::drawFieldBackground(QPainter &p) {
int32 backy = _field.y() - st::sendPadding, backh = _field.height() + 2 * st::sendPadding;
if (_replyToId) {
if (_replyTo && _replyTo->from()->nameVersion > _replyToNameVersion) {
updateReplyToName();
}
backy -= st::replyHeight;
backh += st::replyHeight;
}
p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor->b);
if (_replyToId) {
int32 replyLeft = st::replySkip;
p.drawPixmap(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), App::sprite(), st::replyIcon);
if (_replyTo) {
if (_replyTo->getMedia() && _replyTo->getMedia()->hasReplyPreview()) {
ImagePtr replyPreview = _replyTo->getMedia()->replyPreview();
if (!replyPreview->isNull()) {
QRect to(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
if (replyPreview->width() == replyPreview->height()) {
p.drawPixmap(to.x(), to.y(), replyPreview->pix());
} else {
QRect from = (replyPreview->width() > replyPreview->height()) ? QRect((replyPreview->width() - replyPreview->height()) / 2, 0, replyPreview->height(), replyPreview->height()) : QRect(0, (replyPreview->height() - replyPreview->width()) / 2, replyPreview->width(), replyPreview->width());
p.drawPixmap(to, replyPreview->pix(), from);
}
}
replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
}
p.setPen(st::replyColor->p);
_replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _replyCancel.width() - st::msgReplyPadding.right());
p.setPen((_replyTo->getMedia() ? st::msgInDateColor : st::msgColor)->p);
_replyToText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _replyCancel.width() - st::msgReplyPadding.right());
} else {
p.setFont(st::msgDateFont->f);
p.setPen(st::msgInDateColor->p);
p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->m.elidedText(lang(lng_profile_loading), Qt::ElideRight, width() - replyLeft - _replyCancel.width() - st::msgReplyPadding.right()));
}
}
}
void HistoryWidget::paintEvent(QPaintEvent *e) { void HistoryWidget::paintEvent(QPaintEvent *e) {
QPainter p(this); QPainter p(this);
QRect r(e->rect()); QRect r(e->rect());
@ -3635,7 +3812,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
if (_list) { if (_list) {
if (!_scroll.isHidden()) { if (!_scroll.isHidden()) {
if (!_field.isHidden()) { if (!_field.isHidden()) {
p.fillRect(0, _field.y() - st::sendPadding, width(), _field.height() + 2 * st::sendPadding, st::taMsgField.bgColor->b); drawFieldBackground(p);
} }
} else { } else {
QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - _field.height() - 2 * st::sendPadding - st::msgDogImg.pxHeight()) * 4) / 9); QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - _field.height() - 2 * st::sendPadding - st::msgDogImg.pxHeight()) * 4) / 9);

View file

@ -252,6 +252,8 @@ public:
HistoryWidget(QWidget *parent); HistoryWidget(QWidget *parent);
void start();
void messagesReceived(const MTPmessages_Messages &messages, mtpRequestId requestId); void messagesReceived(const MTPmessages_Messages &messages, mtpRequestId requestId);
void windowShown(); void windowShown();
@ -269,6 +271,7 @@ public:
void contextMenuEvent(QContextMenuEvent *e); void contextMenuEvent(QContextMenuEvent *e);
void updateTopBarSelection(); void updateTopBarSelection();
void checkMentionDropdown();
void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth); void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth);
void topBarShadowParams(int32 &x, float64 &o); void topBarShadowParams(int32 &x, float64 &o);
@ -281,7 +284,7 @@ public:
void peerMessagesUpdated(); void peerMessagesUpdated();
void msgUpdated(PeerId peer, const HistoryItem *msg); void msgUpdated(PeerId peer, const HistoryItem *msg);
void newUnreadMsg(History *history, MsgId msgId); void newUnreadMsg(History *history, HistoryItem *item);
void historyToDown(History *history); void historyToDown(History *history);
void historyWasRead(bool force = true); void historyWasRead(bool force = true);
@ -295,11 +298,11 @@ public:
void destroyData(); void destroyData();
void uploadImage(const QImage &img, bool withText = false); void uploadImage(const QImage &img, bool withText = false);
void uploadFile(const QString &file, bool withText = false); // with confirmation void uploadFile(const QString &file, bool withText = false); // with confirmation
void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText = false); void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText = false);
void uploadConfirmImageUncompressed(bool ctrlShiftEnter); void uploadConfirmImageUncompressed(bool ctrlShiftEnter, MsgId replyTo);
void uploadMedias(const QStringList &files, ToPrepareMediaType type); void uploadMedias(const QStringList &files, ToPrepareMediaType type);
void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer = 0); void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer = 0);
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname); void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo);
void confirmSendImage(const ReadyLocalMedia &img); void confirmSendImage(const ReadyLocalMedia &img);
void cancelSendImage(); void cancelSendImage();
@ -312,7 +315,7 @@ public:
void onShareContact(const PeerId &peer, UserData *contact); void onShareContact(const PeerId &peer, UserData *contact);
void onSendPaths(const PeerId &peer); void onSendPaths(const PeerId &peer);
void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, int32 userId = 0); void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId = 0);
PeerData *peer() const; PeerData *peer() const;
PeerData *activePeer() const; PeerData *activePeer() const;
@ -343,6 +346,9 @@ public:
void updateScrollColors(); void updateScrollColors();
MsgId replyToId() const;
void updateReplyTo(bool force = false);
~HistoryWidget(); ~HistoryWidget();
signals: signals:
@ -353,8 +359,11 @@ signals:
public slots: public slots:
void onCancel(); void onCancel();
void onReplyToMessage();
void onReplyCancel();
void peerUpdated(PeerData *data); void peerUpdated(PeerData *data);
void onPeerLoaded(PeerData *data);
void cancelTyping(); void cancelTyping();
@ -368,7 +377,7 @@ public slots:
void onListScroll(); void onListScroll();
void onHistoryToEnd(); void onHistoryToEnd();
void onSend(bool ctrlShiftEnter = false); void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1);
void onPhotoSelect(); void onPhotoSelect();
void onDocumentSelect(); void onDocumentSelect();
@ -392,6 +401,7 @@ public slots:
void onFieldFocused(); void onFieldFocused();
void onFieldResize(); void onFieldResize();
void onFieldCursorChanged();
void onScrollTimer(); void onScrollTimer();
void onForwardSelected(); void onForwardSelected();
@ -409,11 +419,18 @@ public slots:
private: private:
MsgId _replyToId;
HistoryItem *_replyTo;
Text _replyToName, _replyToText;
int32 _replyToNameVersion;
IconedButton _replyCancel;
void updateReplyToName();
void drawFieldBackground(QPainter &p);
bool messagesFailed(const RPCError &error, mtpRequestId requestId); bool messagesFailed(const RPCError &error, mtpRequestId requestId);
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0); void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0);
void addMessagesToFront(const QVector<MTPMessage> &messages); void addMessagesToFront(const QVector<MTPMessage> &messages);
void addMessagesToBack(const QVector<MTPMessage> &messages); void addMessagesToBack(const QVector<MTPMessage> &messages);
void chatLoaded(const MTPmessages_ChatFull &res);
void stickersGot(const MTPmessages_AllStickers &stickers); void stickersGot(const MTPmessages_AllStickers &stickers);
bool stickersFailed(const RPCError &error); bool stickersFailed(const RPCError &error);
@ -421,7 +438,7 @@ private:
uint64 _lastStickersUpdate; uint64 _lastStickersUpdate;
mtpRequestId _stickersUpdateRequest; mtpRequestId _stickersUpdateRequest;
void writeDraft(const QString *text = 0, const MessageCursor *cursor = 0); void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0);
void setFieldText(const QString &text); void setFieldText(const QString &text);
QStringList getMediasFromMime(const QMimeData *d); QStringList getMediasFromMime(const QMimeData *d);
@ -429,6 +446,7 @@ private:
void updateDragAreas(); void updateDragAreas();
bool _loadingMessages;
int32 histRequestsCount; int32 histRequestsCount;
PeerData *histPeer; PeerData *histPeer;
History *_activeHist; History *_activeHist;
@ -446,6 +464,8 @@ private:
IconedButton _toHistoryEnd; IconedButton _toHistoryEnd;
MentionsDropdown _attachMention;
FlatButton _send; FlatButton _send;
IconedButton _attachDocument, _attachPhoto, _attachEmoji; IconedButton _attachDocument, _attachPhoto, _attachEmoji;
MessageField _field; MessageField _field;
@ -461,9 +481,6 @@ private:
LocalImageLoader imageLoader; LocalImageLoader imageLoader;
bool _synthedTextUpdate; bool _synthedTextUpdate;
PeerId loadingChatId;
mtpRequestId loadingRequestId;
int64 serviceImageCacheSize; int64 serviceImageCacheSize;
QImage confirmImage; QImage confirmImage;
PhotoId confirmImageId; PhotoId confirmImageId;

View file

@ -41,6 +41,7 @@ void LocalImageLoaderPrivate::prepareImages() {
ToPrepareMediaType type; ToPrepareMediaType type;
bool animated = false; bool animated = false;
bool ctrlShiftEnter = false; bool ctrlShiftEnter = false;
MsgId replyTo = 0;
{ {
QMutexLocker lock(loader->toPrepareMutex()); QMutexLocker lock(loader->toPrepareMutex());
ToPrepareMedias &list(loader->toPrepareMedias()); ToPrepareMedias &list(loader->toPrepareMedias());
@ -53,6 +54,7 @@ void LocalImageLoaderPrivate::prepareImages() {
id = list.front().id; id = list.front().id;
type = list.front().type; type = list.front().type;
ctrlShiftEnter = list.front().ctrlShiftEnter; ctrlShiftEnter = list.front().ctrlShiftEnter;
replyTo = list.front().replyTo;
} }
if (img.isNull()) { if (img.isNull()) {
@ -212,7 +214,7 @@ void LocalImageLoaderPrivate::prepareImages() {
{ {
QMutexLocker lock(loader->readyMutex()); QMutexLocker lock(loader->readyMutex());
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter)); loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
} }
{ {
@ -234,11 +236,11 @@ LocalImageLoaderPrivate::~LocalImageLoaderPrivate() {
LocalImageLoader::LocalImageLoader(QObject *parent) : QObject(parent), thread(0), priv(0) { LocalImageLoader::LocalImageLoader(QObject *parent) : QObject(parent), thread(0), priv(0) {
} }
void LocalImageLoader::append(const QStringList &files, const PeerId &peer, ToPrepareMediaType t) { void LocalImageLoader::append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
{ {
QMutexLocker lock(toPrepareMutex()); QMutexLocker lock(toPrepareMutex());
for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) { for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) {
toPrepare.push_back(ToPrepareMedia(*i, peer, t, false)); toPrepare.push_back(ToPrepareMedia(*i, peer, t, false, replyTo));
} }
} }
if (!thread) { if (!thread) {
@ -249,11 +251,11 @@ void LocalImageLoader::append(const QStringList &files, const PeerId &peer, ToPr
emit needToPrepare(); emit needToPrepare();
} }
PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPrepareMediaType t) { PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
PhotoId result = 0; PhotoId result = 0;
{ {
QMutexLocker lock(toPrepareMutex()); QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(img, peer, t, false)); toPrepare.push_back(ToPrepareMedia(img, peer, t, false, replyTo));
result = toPrepare.back().id; result = toPrepare.back().id;
} }
if (!thread) { if (!thread) {
@ -265,11 +267,11 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPr
return result; return result;
} }
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) { PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter) {
PhotoId result = 0; PhotoId result = 0;
{ {
QMutexLocker lock(toPrepareMutex()); QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter)); toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter, replyTo));
result = toPrepare.back().id; result = toPrepare.back().id;
} }
if (!thread) { if (!thread) {
@ -281,11 +283,11 @@ PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepar
return result; return result;
} }
PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, ToPrepareMediaType t) { PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
PhotoId result = 0; PhotoId result = 0;
{ {
QMutexLocker lock(toPrepareMutex()); QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(file, peer, t, false)); toPrepare.push_back(ToPrepareMedia(file, peer, t, false, replyTo));
result = toPrepare.back().id; result = toPrepare.back().id;
} }
if (!thread) { if (!thread) {

View file

@ -25,11 +25,11 @@ enum ToPrepareMediaType {
}; };
struct ToPrepareMedia { struct ToPrepareMedia {
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) { ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
} }
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) { ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
} }
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) { ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
} }
PhotoId id; PhotoId id;
QString file; QString file;
@ -38,13 +38,14 @@ struct ToPrepareMedia {
PeerId peer; PeerId peer;
ToPrepareMediaType type; ToPrepareMediaType type;
bool ctrlShiftEnter; bool ctrlShiftEnter;
MsgId replyTo;
}; };
typedef QList<ToPrepareMedia> ToPrepareMedias; typedef QList<ToPrepareMedia> ToPrepareMedias;
typedef QMap<int32, QByteArray> LocalFileParts; typedef QMap<int32, QByteArray> LocalFileParts;
struct ReadyLocalMedia { struct ReadyLocalMedia {
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter) : ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) { type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
if (!jpeg.isEmpty()) { if (!jpeg.isEmpty()) {
int32 size = jpeg.size(); int32 size = jpeg.size();
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) { for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
@ -54,6 +55,7 @@ struct ReadyLocalMedia {
hashMd5Hex(jpeg.constData(), jpeg.size(), jpeg_md5.data()); hashMd5Hex(jpeg.constData(), jpeg.size(), jpeg_md5.data());
} }
} }
MsgId replyTo;
ToPrepareMediaType type; ToPrepareMediaType type;
QString file, filename; QString file, filename;
int32 filesize; int32 filesize;
@ -103,10 +105,10 @@ class LocalImageLoader : public QObject {
public: public:
LocalImageLoader(QObject *parent); LocalImageLoader(QObject *parent);
void append(const QStringList &files, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto); void append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QByteArray &img, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto); PhotoId append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QImage &img, const PeerId &peer, ToPrepareMediaType t = ToPreparePhoto, bool ctrlShiftEnter = false); PhotoId append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter = false);
PhotoId append(const QString &file, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto); PhotoId append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
QMutex *readyMutex(); QMutex *readyMutex();
ReadyLocalMedias &readyList(); ReadyLocalMedias &readyList();

View file

@ -1558,7 +1558,7 @@ namespace Local {
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
FileReadDescriptor settingsData; FileReadDescriptor settingsData;
if (!readFile(settingsData, qsl("settings"), SafePath)) { if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath)) {
_readOldSettings(); _readOldSettings();
_readOldUserSettings(false); // needed further in _readUserSettings _readOldUserSettings(false); // needed further in _readUserSettings
_readOldMtpData(false); // needed further in _readMtpData _readOldMtpData(false); // needed further in _readMtpData
@ -1617,7 +1617,7 @@ namespace Local {
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
FileWriteDescriptor settings(qsl("settings"), SafePath); FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath);
if (_settingsSalt.isEmpty() || !_settingsKey.created()) { if (_settingsSalt.isEmpty() || !_settingsKey.created()) {
_settingsSalt.resize(LocalEncryptSaltSize); _settingsSalt.resize(LocalEncryptSaltSize);
memset_rand(_settingsSalt.data(), _settingsSalt.size()); memset_rand(_settingsSalt.data(), _settingsSalt.size());
@ -1735,10 +1735,10 @@ namespace Local {
return _oldMapVersion; return _oldMapVersion;
} }
void writeDraft(const PeerId &peer, const QString &text) { void writeDraft(const PeerId &peer, const MessageDraft &draft) {
if (!_working()) return; if (!_working()) return;
if (text.isEmpty()) { if (draft.replyTo <= 0 && draft.text.isEmpty()) {
DraftsMap::iterator i = _draftsMap.find(peer); DraftsMap::iterator i = _draftsMap.find(peer);
if (i != _draftsMap.cend()) { if (i != _draftsMap.cend()) {
clearKey(i.value()); clearKey(i.value());
@ -1755,8 +1755,8 @@ namespace Local {
_mapChanged = true; _mapChanged = true;
_writeMap(WriteMapFast); _writeMap(WriteMapFast);
} }
EncryptedDescriptor data(sizeof(quint64) + _stringSize(text)); EncryptedDescriptor data(sizeof(quint64) + _stringSize(draft.text) + sizeof(qint32));
data.stream << quint64(peer) << text; data.stream << quint64(peer) << draft.text << qint32(draft.replyTo);
FileWriteDescriptor file(i.value()); FileWriteDescriptor file(i.value());
file.writeEncrypted(data); file.writeEncrypted(data);
@ -1764,24 +1764,26 @@ namespace Local {
} }
} }
QString readDraft(const PeerId &peer) { MessageDraft readDraft(const PeerId &peer) {
if (!_draftsNotReadMap.remove(peer)) return QString(); if (!_draftsNotReadMap.remove(peer)) return MessageDraft();
DraftsMap::iterator j = _draftsMap.find(peer); DraftsMap::iterator j = _draftsMap.find(peer);
if (j == _draftsMap.cend()) { if (j == _draftsMap.cend()) {
return QString(); return MessageDraft();
} }
FileReadDescriptor draft; FileReadDescriptor draft;
if (!readEncryptedFile(draft, j.value())) { if (!readEncryptedFile(draft, j.value())) {
clearKey(j.value()); clearKey(j.value());
_draftsMap.erase(j); _draftsMap.erase(j);
return QString(); return MessageDraft();
} }
quint64 draftPeer; quint64 draftPeer;
QString draftText; QString draftText;
qint32 draftReplyTo = 0;
draft.stream >> draftPeer >> draftText; draft.stream >> draftPeer >> draftText;
return (draftPeer == peer) ? draftText : QString(); if (draft.version >= 7021) draft.stream >> draftReplyTo;
return (draftPeer == peer) ? MessageDraft(MsgId(draftReplyTo), draftText) : MessageDraft();
} }
void writeDraftPositions(const PeerId &peer, const MessageCursor &cur) { void writeDraftPositions(const PeerId &peer, const MessageCursor &cur) {
@ -2092,6 +2094,7 @@ namespace Local {
quint32 size = 0; quint32 size = 0;
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) { for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
DocumentData *doc = i->first; DocumentData *doc = i->first;
if (doc->status == FileFailed) continue;
// id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt // id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt
size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->alt); size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->alt);
@ -2099,6 +2102,7 @@ namespace Local {
EncryptedDescriptor data(size); EncryptedDescriptor data(size);
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) { for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
DocumentData *doc = i->first; DocumentData *doc = i->first;
if (doc->status == FileFailed) continue;
data.stream << quint64(doc->id) << qint16(i->second) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->alt; data.stream << quint64(doc->id) << qint16(i->second) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->alt;
} }
@ -2126,7 +2130,7 @@ namespace Local {
qint32 date, dc, size, width, height, type; qint32 date, dc, size, width, height, type;
qint16 value; qint16 value;
stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type; stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type;
if (stickers.version >= AppVersion) { if (stickers.version >= 7021) {
stickers.stream >> alt; stickers.stream >> alt;
} }
if (read.contains(id)) continue; if (read.contains(id)) continue;

View file

@ -100,8 +100,14 @@ namespace Local {
ReadMapState readMap(const QByteArray &pass); ReadMapState readMap(const QByteArray &pass);
int32 oldMapVersion(); int32 oldMapVersion();
void writeDraft(const PeerId &peer, const QString &text); struct MessageDraft {
QString readDraft(const PeerId &peer); MessageDraft(MsgId replyTo = 0, QString text = QString()) : replyTo(replyTo), text(text) {
}
MsgId replyTo;
QString text;
};
void writeDraft(const PeerId &peer, const MessageDraft &draft);
MessageDraft readDraft(const PeerId &peer);
void writeDraftPositions(const PeerId &peer, const MessageCursor &cur); void writeDraftPositions(const PeerId &peer, const MessageCursor &cur);
MessageCursor readDraftPositions(const PeerId &peer); MessageCursor readDraftPositions(const PeerId &peer);
bool hasDraftPositions(const PeerId &peer); bool hasDraftPositions(const PeerId &peer);

View file

@ -40,7 +40,7 @@ namespace {
QDateTime tm(QDateTime::currentDateTime()); QDateTime tm(QDateTime::currentDateTime());
QThread *thread = QThread::currentThread(); QThread *thread = QThread::currentThread();
MTPThread *mtpThread = dynamic_cast<MTPThread*>(thread); MTPThread *mtpThread = qobject_cast<MTPThread*>(thread);
uint32 threadId = mtpThread ? mtpThread->getThreadId() : 0; uint32 threadId = mtpThread ? mtpThread->getThreadId() : 0;
return QString("[%1 %2-%3]").arg(tm.toString("hh:mm:ss.zzz")).arg(QString("%1").arg(threadId, 2, 10, zero)).arg(++logEntry, 7, 10, zero); return QString("[%1 %2-%3]").arg(tm.toString("hh:mm:ss.zzz")).arg(QString("%1").arg(threadId, 2, 10, zero)).arg(++logEntry, 7, 10, zero);

View file

@ -350,11 +350,10 @@ MainWidget *TopBarWidget::main() {
MainWidget::MainWidget(Window *window) : QWidget(window), _started(0), failedObjId(0), _dialogsWidth(st::dlgMinWidth), MainWidget::MainWidget(Window *window) : QWidget(window), _started(0), failedObjId(0), _dialogsWidth(st::dlgMinWidth),
dialogs(this), history(this), profile(0), overview(0), _topBar(this), _forwardConfirm(0), hider(0), _mediaType(this), _mediaTypeMask(0), dialogs(this), history(this), profile(0), overview(0), _topBar(this), _forwardConfirm(0), hider(0), _mediaType(this), _mediaTypeMask(0),
updGoodPts(0), updLastPts(0), updPtsCount(0), updDate(0), updQts(-1), updSeq(0), updInited(false), _onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false), updGoodPts(0), updLastPts(0), updPtsCount(0), updDate(0), updQts(-1), updSeq(0), updInited(false), updSkipPtsUpdateLevel(0), _onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false),
_failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0) { _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) {
setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
App::initBackground();
updateScrollColors(); updateScrollColors();
connect(window, SIGNAL(resized(const QSize &)), this, SLOT(onParentResize(const QSize &))); connect(window, SIGNAL(resized(const QSize &)), this, SLOT(onParentResize(const QSize &)));
@ -401,7 +400,7 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
show(); show();
setFocus(); setFocus();
App::initMedia(); _api->init();
} }
mtpRequestId MainWidget::onForward(const PeerId &peer, bool forwardSelected) { mtpRequestId MainWidget::onForward(const PeerId &peer, bool forwardSelected) {
@ -579,6 +578,10 @@ void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHis
MTP::send(MTPmessages_DeleteHistory(peer->input, d.voffset), rpcDone(&MainWidget::deleteHistoryPart, peer)); MTP::send(MTPmessages_DeleteHistory(peer->input, d.voffset), rpcDone(&MainWidget::deleteHistoryPart, peer));
} }
void MainWidget::deleteMessages(const QVector<MTPint> &ids) {
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(ids)), rpcDone(&MainWidget::msgsWereDeleted));
}
void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) { void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) {
const MTPDcontacts_link &d(result.c_contacts_link()); const MTPDcontacts_link &d(result.c_contacts_link());
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser)); App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
@ -780,8 +783,9 @@ DialogsIndexed &MainWidget::contactsList() {
return dialogs.contactsList(); return dialogs.contactsList();
} }
void MainWidget::sendPreparedText(History *hist, const QString &text) { void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo) {
QString sendingText, leftText = text; QString sendingText, leftText = text;
if (replyTo < 0) replyTo = history.replyToId();
while (textSplit(sendingText, leftText, MaxMessageSize)) { while (textSplit(sendingText, leftText, MaxMessageSize)) {
MsgId newId = clientMsgId(); MsgId newId = clientMsgId();
uint64 randomId = MTP::nonce<uint64>(); uint64 randomId = MTP::nonce<uint64>();
@ -789,9 +793,10 @@ void MainWidget::sendPreparedText(History *hist, const QString &text) {
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
MTPstring msgText(MTP_string(sendingText)); MTPstring msgText(MTP_string(sendingText));
int32 flags = (hist->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out), replyToMsgId = 0; int32 flags = (hist->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out);
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty())); if (replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, MTP_int(replyToMsgId), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
} }
historyToDown(hist); historyToDown(hist);
@ -800,10 +805,10 @@ void MainWidget::sendPreparedText(History *hist, const QString &text) {
} }
} }
void MainWidget::sendMessage(History *hist, const QString &text) { void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo) {
readServerHistory(hist, false); readServerHistory(hist, false);
hist->loadAround(0); hist->loadAround(0);
sendPreparedText(hist, history.prepareMessage(text)); sendPreparedText(hist, history.prepareMessage(text), replyTo);
} }
void MainWidget::readServerHistory(History *hist, bool force) { void MainWidget::readServerHistory(History *hist, bool force) {
@ -873,7 +878,7 @@ void MainWidget::overviewPreloaded(PeerData *peer, const MTPmessages_Messages &r
const MTPDmessages_messages &d(result.c_messages_messages()); const MTPDmessages_messages &d(result.c_messages_messages());
App::feedUsers(d.vusers); App::feedUsers(d.vusers);
App::feedChats(d.vchats); App::feedChats(d.vchats);
h->_overviewCount[type] = 0; h->_overviewCount[type] = d.vmessages.c_vector().v.size();
} break; } break;
case mtpc_messages_messagesSlice: { case mtpc_messages_messagesSlice: {
@ -937,6 +942,7 @@ void MainWidget::changingMsgId(HistoryItem *row, MsgId newId) {
} }
void MainWidget::itemRemoved(HistoryItem *item) { void MainWidget::itemRemoved(HistoryItem *item) {
api()->itemRemoved(item);
dialogs.itemRemoved(item); dialogs.itemRemoved(item);
if (history.peer() == item->history()->peer) { if (history.peer() == item->history()->peer) {
history.itemRemoved(item); history.itemRemoved(item);
@ -945,6 +951,7 @@ void MainWidget::itemRemoved(HistoryItem *item) {
} }
void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) { void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
api()->itemReplaced(oldItem, newItem);
dialogs.itemReplaced(oldItem, newItem); dialogs.itemReplaced(oldItem, newItem);
if (history.peer() == newItem->history()->peer) { if (history.peer() == newItem->history()->peer) {
history.itemReplaced(oldItem, newItem); history.itemReplaced(oldItem, newItem);
@ -1086,6 +1093,11 @@ void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &
} }
} }
void MainWidget::msgsWereDeleted(const MTPmessages_AffectedMessages &result) {
const MTPDmessages_affectedMessages &d(result.c_messages_affectedMessages());
updPtsUpdated(d.vpts.v, d.vpts_count.v);
}
void MainWidget::videoLoadProgress(mtpFileLoader *loader) { void MainWidget::videoLoadProgress(mtpFileLoader *loader) {
VideoData *video = App::video(loader->objId()); VideoData *video = App::video(loader->objId());
if (video->loader) { if (video->loader) {
@ -1240,7 +1252,10 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) { void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) {
loadFailed(loader, started, SLOT(documentLoadRetry())); loadFailed(loader, started, SLOT(documentLoadRetry()));
DocumentData *document = App::document(loader->objId()); DocumentData *document = App::document(loader->objId());
if (document && document->loader) document->finish(); if (document) {
if (document->loader) document->finish();
document->status = FileFailed;
}
} }
void MainWidget::documentLoadRetry() { void MainWidget::documentLoadRetry() {
@ -1260,16 +1275,18 @@ void MainWidget::updateOnlineDisplay() {
if (App::wnd()->settingsWidget()) App::wnd()->settingsWidget()->updateOnlineDisplay(); if (App::wnd()->settingsWidget()) App::wnd()->settingsWidget()->updateOnlineDisplay();
} }
void MainWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname) { void MainWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo) {
history.confirmShareContact(ctrlShiftEnter, phone, fname, lname); history.confirmShareContact(ctrlShiftEnter, phone, fname, lname, replyTo);
} }
void MainWidget::confirmSendImage(const ReadyLocalMedia &img) { void MainWidget::confirmSendImage(const ReadyLocalMedia &img) {
history.confirmSendImage(img); history.confirmSendImage(img);
history.onReplyCancel();
} }
void MainWidget::confirmSendImageUncompressed(bool ctrlShiftEnter) { void MainWidget::confirmSendImageUncompressed(bool ctrlShiftEnter, MsgId replyTo) {
history.uploadConfirmImageUncompressed(ctrlShiftEnter); history.uploadConfirmImageUncompressed(ctrlShiftEnter, replyTo);
history.onReplyCancel();
} }
void MainWidget::cancelSendImage() { void MainWidget::cancelSendImage() {
@ -1402,6 +1419,14 @@ ImagePtr MainWidget::newBackgroundThumb() {
return _background ? _background->thumb : ImagePtr(); return _background ? _background->thumb : ImagePtr();
} }
ApiWrap *MainWidget::api() {
return _api;
}
void MainWidget::updateReplyTo() {
history.updateReplyTo(true);
}
void MainWidget::setInnerFocus() { void MainWidget::setInnerFocus() {
if (hider || !history.peer()) { if (hider || !history.peer()) {
if (hider && hider->wasOffered()) { if (hider && hider->wasOffered()) {
@ -1755,7 +1780,9 @@ void MainWidget::sentFullDataReceived(uint64 randomId, const MTPmessages_StatedM
} }
} }
if (!randomId) { if (!randomId) {
++updSkipPtsUpdateLevel;
feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts, d.vpts_count)); feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts, d.vpts_count));
--updSkipPtsUpdateLevel;
} }
} break; } break;
@ -1774,7 +1801,9 @@ void MainWidget::sentFullDataReceived(uint64 randomId, const MTPmessages_StatedM
} }
} }
if (!randomId) { if (!randomId) {
++updSkipPtsUpdateLevel;
feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts, d.vpts_count)); feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts, d.vpts_count));
--updSkipPtsUpdateLevel;
} }
App::feedUserLinks(d.vlinks); App::feedUserLinks(d.vlinks);
} break; } break;
@ -1794,7 +1823,7 @@ void MainWidget::sentFullDatasReceived(const MTPmessages_StatedMessages &result)
App::feedUsers(d.vusers); App::feedUsers(d.vusers);
App::feedChats(d.vchats); App::feedChats(d.vchats);
App::feedMsgs(d.vmessages, true); App::feedMsgs(d.vmessages, 1);
history.peerMessagesUpdated(); history.peerMessagesUpdated();
} break; } break;
@ -1810,7 +1839,7 @@ void MainWidget::sentFullDatasReceived(const MTPmessages_StatedMessages &result)
App::feedUsers(d.vusers); App::feedUsers(d.vusers);
App::feedChats(d.vchats); App::feedChats(d.vchats);
App::feedMsgs(d.vmessages, true); App::feedMsgs(d.vmessages, 1);
history.peerMessagesUpdated(); history.peerMessagesUpdated();
App::feedUserLinks(d.vlinks); App::feedUserLinks(d.vlinks);
@ -1841,8 +1870,8 @@ void MainWidget::dialogsToUp() {
dialogs.dialogsToUp(); dialogs.dialogsToUp();
} }
void MainWidget::newUnreadMsg(History *hist, MsgId msgId) { void MainWidget::newUnreadMsg(History *hist, HistoryItem *item) {
history.newUnreadMsg(hist, msgId); history.newUnreadMsg(hist, item);
} }
void MainWidget::historyWasRead() { void MainWidget::historyWasRead() {
@ -2184,6 +2213,7 @@ uint64 MainWidget::ptsKey(PtsSkippedQueue queue) {
void MainWidget::applySkippedPtsUpdates() { void MainWidget::applySkippedPtsUpdates() {
if (_byPtsTimer.isActive()) _byPtsTimer.stop(); if (_byPtsTimer.isActive()) _byPtsTimer.stop();
if (_byPtsQueue.isEmpty()) return; if (_byPtsQueue.isEmpty()) return;
++updSkipPtsUpdateLevel;
for (QMap<uint64, PtsSkippedQueue>::const_iterator i = _byPtsQueue.cbegin(), e = _byPtsQueue.cend(); i != e; ++i) { for (QMap<uint64, PtsSkippedQueue>::const_iterator i = _byPtsQueue.cbegin(), e = _byPtsQueue.cend(); i != e; ++i) {
switch (i.value()) { switch (i.value()) {
case SkippedUpdate: feedUpdate(_byPtsUpdate.value(i.key())); break; case SkippedUpdate: feedUpdate(_byPtsUpdate.value(i.key())); break;
@ -2193,6 +2223,7 @@ void MainWidget::applySkippedPtsUpdates() {
case SkippedStatedMessages: sentFullDatasReceived(_byPtsStatedMessages.value(i.key())); break; case SkippedStatedMessages: sentFullDatasReceived(_byPtsStatedMessages.value(i.key())); break;
} }
} }
--updSkipPtsUpdateLevel;
clearSkippedPtsUpdates(); clearSkippedPtsUpdates();
} }
@ -2206,7 +2237,7 @@ void MainWidget::clearSkippedPtsUpdates() {
} }
bool MainWidget::updPtsUpdated(int pts, int ptsCount) { // return false if need to save that update and apply later bool MainWidget::updPtsUpdated(int pts, int ptsCount) { // return false if need to save that update and apply later
if (!updInited) return true; if (!updInited || updSkipPtsUpdateLevel) return true;
updLastPts = qMax(updLastPts, pts); updLastPts = qMax(updLastPts, pts);
updPtsCount += ptsCount; updPtsCount += ptsCount;
@ -2227,7 +2258,7 @@ void MainWidget::feedDifference(const MTPVector<MTPUser> &users, const MTPVector
App::feedUsers(users); App::feedUsers(users);
App::feedChats(chats); App::feedChats(chats);
feedMessageIds(other); feedMessageIds(other);
App::feedMsgs(msgs, true); App::feedMsgs(msgs, 1);
feedUpdates(other, true); feedUpdates(other, true);
history.peerMessagesUpdated(); history.peerMessagesUpdated();
} }
@ -2286,7 +2317,7 @@ void MainWidget::start(const MTPUser &user) {
_started = true; _started = true;
App::wnd()->sendServiceHistoryRequest(); App::wnd()->sendServiceHistoryRequest();
Local::readRecentStickers(); Local::readRecentStickers();
history.updateRecentStickers(); history.start();
} }
bool MainWidget::started() { bool MainWidget::started() {
@ -2300,18 +2331,27 @@ void MainWidget::openLocalUrl(const QString &url) {
} }
} }
void MainWidget::openUserByName(const QString &username) { void MainWidget::openUserByName(const QString &username, bool toProfile) {
UserData *user = App::userByName(username); UserData *user = App::userByName(username);
if (user) { if (user) {
emit showPeerAsync(user->id, 0, false, true); if (toProfile) {
showPeerProfile(user);
} else {
emit showPeerAsync(user->id, 0, false, true);
}
} else { } else {
MTP::send(MTPcontacts_ResolveUsername(MTP_string(username)), rpcDone(&MainWidget::usernameResolveDone), rpcFail(&MainWidget::usernameResolveFail, username)); MTP::send(MTPcontacts_ResolveUsername(MTP_string(username)), rpcDone(&MainWidget::usernameResolveDone, toProfile), rpcFail(&MainWidget::usernameResolveFail, username));
} }
} }
void MainWidget::usernameResolveDone(const MTPUser &user) { void MainWidget::usernameResolveDone(bool toProfile, const MTPUser &user) {
App::wnd()->hideLayer(); App::wnd()->hideLayer();
showPeer(App::feedUsers(MTP_vector<MTPUser>(1, user))->id, 0, false, true); UserData *u = App::feedUsers(MTP_vector<MTPUser>(1, user));
if (toProfile) {
showPeerProfile(u);
} else {
showPeer(u->id, 0, false, true);
}
} }
bool MainWidget::usernameResolveFail(QString name, const RPCError &error) { bool MainWidget::usernameResolveFail(QString name, const RPCError &error) {
@ -2535,7 +2575,7 @@ MainWidget::~MainWidget() {
delete hider; delete hider;
MTP::clearGlobalHandlers(); MTP::clearGlobalHandlers();
App::deinitMedia(false); delete _api;
if (App::wnd()) App::wnd()->noMain(this); if (App::wnd()) App::wnd()->noMain(this);
} }
@ -2664,10 +2704,9 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
_byPtsUpdates.insert(ptsKey(SkippedUpdates), updates); _byPtsUpdates.insert(ptsKey(SkippedUpdates), updates);
return; return;
} }
if (!App::userLoaded(d.vuser_id.v)) return getDifference();
if (!App::userLoaded(d.vfrom_id.v)) return getDifference(); bool out = (d.vflags.v & MTPDmessage_flag_out);
int32 flags = MTPDmessage_flag_unread; HistoryItem *item = App::histories().addToBack(MTP_message(d.vflags, d.vid, out ? MTP_int(MTP::authedId()) : d.vuser_id, MTP_peerUser(out ? d.vuser_id : MTP_int(MTP::authedId())), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty()));
HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerUser(MTP_int(MTP::authedId())), MTPint(), MTPint(), MTPint(), d.vdate, d.vmessage, MTP_messageMediaEmpty()));
if (item) { if (item) {
history.peerMessagesUpdated(item->history()->peer->id); history.peerMessagesUpdated(item->history()->peer->id);
} }
@ -2681,10 +2720,8 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
_byPtsUpdates.insert(ptsKey(SkippedUpdates), updates); _byPtsUpdates.insert(ptsKey(SkippedUpdates), updates);
return; return;
} }
if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v) || (d.has_fwd_from_id() && !App::userLoaded(d.vfwd_from_id.v))) return getDifference();
if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v)) return getDifference(); HistoryItem *item = App::histories().addToBack(MTP_message(d.vflags, d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from_id, d.vfwd_date, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty()));
int32 flags = MTPDmessage_flag_unread; // unread
HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), MTPint(), MTPint(), MTPint(), d.vdate, d.vmessage, MTP_messageMediaEmpty()));
if (item) { if (item) {
history.peerMessagesUpdated(item->history()->peer->id); history.peerMessagesUpdated(item->history()->peer->id);
} }
@ -2897,7 +2934,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
UserData *user = App::userLoaded(d.vuser_id.v); UserData *user = App::userLoaded(d.vuser_id.v);
if (user) { if (user) {
if (App::history(user->id)->loadedAtBottom()) { if (App::history(user->id)->loadedAtBottom()) {
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lng_action_user_registered(lt_from, user->name), false, true); App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lng_action_user_registered(lt_from, user->name), MTPDmessage_flag_unread);
} }
} }
} break; } break;

View file

@ -17,13 +17,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include <QtWidgets/QWidget>
#include "gui/flatbutton.h"
#include "dialogswidget.h" #include "dialogswidget.h"
#include "historywidget.h" #include "historywidget.h"
#include "profilewidget.h" #include "profilewidget.h"
#include "overviewwidget.h" #include "overviewwidget.h"
#include "apiwrap.h"
class Window; class Window;
struct DialogRow; struct DialogRow;
@ -187,7 +185,7 @@ public:
void start(const MTPUser &user); void start(const MTPUser &user);
void openLocalUrl(const QString &str); void openLocalUrl(const QString &str);
void openUserByName(const QString &name); void openUserByName(const QString &name, bool toProfile = false);
void startFull(const MTPVector<MTPUser> &users); void startFull(const MTPVector<MTPUser> &users);
bool started(); bool started();
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0); void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
@ -213,7 +211,7 @@ public:
void msgUpdated(PeerId peer, const HistoryItem *msg); void msgUpdated(PeerId peer, const HistoryItem *msg);
void historyToDown(History *hist); void historyToDown(History *hist);
void dialogsToUp(); void dialogsToUp();
void newUnreadMsg(History *history, MsgId msgId); void newUnreadMsg(History *history, HistoryItem *item);
void historyWasRead(); void historyWasRead();
void peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg); void peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg);
@ -229,9 +227,9 @@ public:
void showBackFromStack(); void showBackFromStack();
QRect historyRect() const; QRect historyRect() const;
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname); void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo);
void confirmSendImage(const ReadyLocalMedia &img); void confirmSendImage(const ReadyLocalMedia &img);
void confirmSendImageUncompressed(bool ctrlShiftEnter); void confirmSendImageUncompressed(bool ctrlShiftEnter, MsgId replyTo);
void cancelSendImage(); void cancelSendImage();
void destroyData(); void destroyData();
@ -262,6 +260,7 @@ public:
bool leaveChatFailed(PeerData *peer, const RPCError &e); bool leaveChatFailed(PeerData *peer, const RPCError &e);
void deleteHistory(PeerData *peer, const MTPmessages_StatedMessage &result); void deleteHistory(PeerData *peer, const MTPmessages_StatedMessage &result);
void deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result); void deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result);
void deleteMessages(const QVector<MTPint> &ids);
void deletedContact(UserData *user, const MTPcontacts_Link &result); void deletedContact(UserData *user, const MTPcontacts_Link &result);
void deleteHistoryAndContact(UserData *user, const MTPcontacts_Link &result); void deleteHistoryAndContact(UserData *user, const MTPcontacts_Link &result);
void clearHistory(PeerData *peer); void clearHistory(PeerData *peer);
@ -286,8 +285,8 @@ public:
DialogsIndexed &contactsList(); DialogsIndexed &contactsList();
void sendMessage(History *history, const QString &text); void sendMessage(History *history, const QString &text, MsgId replyTo);
void sendPreparedText(History *hist, const QString &text); void sendPreparedText(History *hist, const QString &text, MsgId replyTo);
void readServerHistory(History *history, bool force = true); void readServerHistory(History *history, bool force = true);
@ -324,6 +323,9 @@ public:
bool chatBackgroundLoading(); bool chatBackgroundLoading();
void checkChatBackground(); void checkChatBackground();
ImagePtr newBackgroundThumb(); ImagePtr newBackgroundThumb();
ApiWrap *api();
void updateReplyTo();
~MainWidget(); ~MainWidget();
@ -382,6 +384,7 @@ public slots:
private: private:
void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result); void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result);
void msgsWereDeleted(const MTPmessages_AffectedMessages &result);
void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req);
bool _started; bool _started;
@ -406,7 +409,7 @@ private:
void handleUpdates(const MTPUpdates &updates); void handleUpdates(const MTPUpdates &updates);
bool updateFail(const RPCError &e); bool updateFail(const RPCError &e);
void usernameResolveDone(const MTPUser &user); void usernameResolveDone(bool toProfile, const MTPUser &user);
bool usernameResolveFail(QString name, const RPCError &error); bool usernameResolveFail(QString name, const RPCError &error);
void hideAll(); void hideAll();
@ -437,6 +440,7 @@ private:
int updGoodPts, updLastPts, updPtsCount; int updGoodPts, updLastPts, updPtsCount;
int updDate, updQts, updSeq; int updDate, updQts, updSeq;
bool updInited; bool updInited;
int updSkipPtsUpdateLevel;
SingleTimer noUpdatesTimer; SingleTimer noUpdatesTimer;
mtpRequestId _onlineRequest; mtpRequestId _onlineRequest;
@ -488,4 +492,6 @@ private:
App::WallPaper *_background; App::WallPaper *_background;
ApiWrap *_api;
}; };

View file

@ -116,19 +116,27 @@ with open('scheme.tl') as f:
prms = {}; prms = {};
conditions = {}; conditions = {};
prmsList = []; prmsList = [];
isTemplate = hasFlags = ''; isTemplate = hasFlags = hasTemplate = '';
for param in paramsList: for param in paramsList:
if (re.match(r'^\s*$', param)): if (re.match(r'^\s*$', param)):
continue; continue;
pnametype = re.match(r'([a-z_][a-z0-9_]*):([A-Za-z0-9<>\._]+|!X|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param); templ = re.match(r'^{([A-Za-z]+):Type}$', param);
if (templ):
hasTemplate = templ.group(1);
continue;
pnametype = re.match(r'([a-z_][a-z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param);
if (not pnametype): if (not pnametype):
print('Bad param found: "' + param + '" in line: ' + line); print('Bad param found: "' + param + '" in line: ' + line);
continue; continue;
pname = pnametype.group(1); pname = pnametype.group(1);
ptypewide = pnametype.group(2); ptypewide = pnametype.group(2);
if (ptypewide == '!X'): if (re.match(r'^!([a-zA-Z]+)$', ptypewide)):
isTemplate = pname; if ('!' + hasTemplate == ptypewide):
ptype = 'TQueryType'; isTemplate = pname;
ptype = 'TQueryType';
else:
print('Bad template param name: "' + param + '" in line: ' + line);
continue;
elif (ptypewide == '#'): elif (ptypewide == '#'):
hasFlags = pname; hasFlags = pname;
ptype = 'int'; ptype = 'int';

View file

@ -23,6 +23,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
enum { enum {
MTPDmessage_flag_unread = (1 << 0), MTPDmessage_flag_unread = (1 << 0),
MTPDmessage_flag_out = (1 << 1), MTPDmessage_flag_out = (1 << 1),
MTPDmessage_flag_notify_by_from = (1 << 4),
}; };
#include "mtproto/mtpPublicRSA.h" #include "mtproto/mtpPublicRSA.h"
@ -59,6 +60,8 @@ class MTProtoConnectionPrivate;
class MTPSessionData; class MTPSessionData;
class MTPThread : public QThread { class MTPThread : public QThread {
Q_OBJECT
public: public:
MTPThread(QObject *parent = 0); MTPThread(QObject *parent = 0);
uint32 getThreadId() const; uint32 getThreadId() const;

File diff suppressed because it is too large Load diff

View file

@ -222,8 +222,8 @@ enum {
mtpc_updates_difference = 0xf49ca0, mtpc_updates_difference = 0xf49ca0,
mtpc_updates_differenceSlice = 0xa8fb1981, mtpc_updates_differenceSlice = 0xa8fb1981,
mtpc_updatesTooLong = 0xe317af7e, mtpc_updatesTooLong = 0xe317af7e,
mtpc_updateShortMessage = 0xb87da3b1, mtpc_updateShortMessage = 0xed5c2127,
mtpc_updateShortChatMessage = 0x20e85ded, mtpc_updateShortChatMessage = 0x52238b3c,
mtpc_updateShort = 0x78d4dec1, mtpc_updateShort = 0x78d4dec1,
mtpc_updatesCombined = 0x725b04c3, mtpc_updatesCombined = 0x725b04c3,
mtpc_updates = 0x74ae4240, mtpc_updates = 0x74ae4240,
@ -5461,8 +5461,8 @@ private:
explicit MTPupdates(MTPDupdates *_data); explicit MTPupdates(MTPDupdates *_data);
friend MTPupdates MTP_updatesTooLong(); friend MTPupdates MTP_updatesTooLong();
friend MTPupdates MTP_updateShortMessage(MTPint _id, MTPint _from_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date); friend MTPupdates MTP_updateShortMessage(MTPint _flags, MTPint _id, MTPint _user_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date, MTPint _fwd_from_id, MTPint _fwd_date, MTPint _reply_to_msg_id);
friend MTPupdates MTP_updateShortChatMessage(MTPint _id, MTPint _from_id, MTPint _chat_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date); friend MTPupdates MTP_updateShortChatMessage(MTPint _flags, MTPint _id, MTPint _from_id, MTPint _chat_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date, MTPint _fwd_from_id, MTPint _fwd_date, MTPint _reply_to_msg_id);
friend MTPupdates MTP_updateShort(const MTPUpdate &_update, MTPint _date); friend MTPupdates MTP_updateShort(const MTPUpdate &_update, MTPint _date);
friend MTPupdates MTP_updatesCombined(const MTPVector<MTPUpdate> &_updates, const MTPVector<MTPUser> &_users, const MTPVector<MTPChat> &_chats, MTPint _date, MTPint _seq_start, MTPint _seq); friend MTPupdates MTP_updatesCombined(const MTPVector<MTPUpdate> &_updates, const MTPVector<MTPUser> &_users, const MTPVector<MTPChat> &_chats, MTPint _date, MTPint _seq_start, MTPint _seq);
friend MTPupdates MTP_updates(const MTPVector<MTPUpdate> &_updates, const MTPVector<MTPUser> &_users, const MTPVector<MTPChat> &_chats, MTPint _date, MTPint _seq); friend MTPupdates MTP_updates(const MTPVector<MTPUpdate> &_updates, const MTPVector<MTPUser> &_users, const MTPVector<MTPChat> &_chats, MTPint _date, MTPint _seq);
@ -8264,14 +8264,14 @@ public:
MTPMessageMedia vmedia; MTPMessageMedia vmedia;
enum { enum {
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_from_id = (1 << 2), flag_fwd_from_id = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
}; };
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; } bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
}; };
class MTPDmessageService : public mtpDataImpl<MTPDmessageService> { class MTPDmessageService : public mtpDataImpl<MTPDmessageService> {
@ -9373,24 +9373,39 @@ class MTPDupdateShortMessage : public mtpDataImpl<MTPDupdateShortMessage> {
public: public:
MTPDupdateShortMessage() { MTPDupdateShortMessage() {
} }
MTPDupdateShortMessage(MTPint _id, MTPint _from_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date) : vid(_id), vfrom_id(_from_id), vmessage(_message), vpts(_pts), vpts_count(_pts_count), vdate(_date) { MTPDupdateShortMessage(MTPint _flags, MTPint _id, MTPint _user_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date, MTPint _fwd_from_id, MTPint _fwd_date, MTPint _reply_to_msg_id) : vflags(_flags), vid(_id), vuser_id(_user_id), vmessage(_message), vpts(_pts), vpts_count(_pts_count), vdate(_date), vfwd_from_id(_fwd_from_id), vfwd_date(_fwd_date), vreply_to_msg_id(_reply_to_msg_id) {
} }
MTPint vflags;
MTPint vid; MTPint vid;
MTPint vfrom_id; MTPint vuser_id;
MTPstring vmessage; MTPstring vmessage;
MTPint vpts; MTPint vpts;
MTPint vpts_count; MTPint vpts_count;
MTPint vdate; MTPint vdate;
MTPint vfwd_from_id;
MTPint vfwd_date;
MTPint vreply_to_msg_id;
enum {
flag_fwd_from_id = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
};
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
}; };
class MTPDupdateShortChatMessage : public mtpDataImpl<MTPDupdateShortChatMessage> { class MTPDupdateShortChatMessage : public mtpDataImpl<MTPDupdateShortChatMessage> {
public: public:
MTPDupdateShortChatMessage() { MTPDupdateShortChatMessage() {
} }
MTPDupdateShortChatMessage(MTPint _id, MTPint _from_id, MTPint _chat_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date) : vid(_id), vfrom_id(_from_id), vchat_id(_chat_id), vmessage(_message), vpts(_pts), vpts_count(_pts_count), vdate(_date) { MTPDupdateShortChatMessage(MTPint _flags, MTPint _id, MTPint _from_id, MTPint _chat_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date, MTPint _fwd_from_id, MTPint _fwd_date, MTPint _reply_to_msg_id) : vflags(_flags), vid(_id), vfrom_id(_from_id), vchat_id(_chat_id), vmessage(_message), vpts(_pts), vpts_count(_pts_count), vdate(_date), vfwd_from_id(_fwd_from_id), vfwd_date(_fwd_date), vreply_to_msg_id(_reply_to_msg_id) {
} }
MTPint vflags;
MTPint vid; MTPint vid;
MTPint vfrom_id; MTPint vfrom_id;
MTPint vchat_id; MTPint vchat_id;
@ -9398,6 +9413,19 @@ public:
MTPint vpts; MTPint vpts;
MTPint vpts_count; MTPint vpts_count;
MTPint vdate; MTPint vdate;
MTPint vfwd_from_id;
MTPint vfwd_date;
MTPint vreply_to_msg_id;
enum {
flag_fwd_from_id = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
};
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
}; };
class MTPDupdateShort : public mtpDataImpl<MTPDupdateShort> { class MTPDupdateShort : public mtpDataImpl<MTPDupdateShort> {
@ -21053,11 +21081,11 @@ inline uint32 MTPupdates::innerLength() const {
switch (_type) { switch (_type) {
case mtpc_updateShortMessage: { case mtpc_updateShortMessage: {
const MTPDupdateShortMessage &v(c_updateShortMessage()); const MTPDupdateShortMessage &v(c_updateShortMessage());
return v.vid.innerLength() + v.vfrom_id.innerLength() + v.vmessage.innerLength() + v.vpts.innerLength() + v.vpts_count.innerLength() + v.vdate.innerLength(); return v.vflags.innerLength() + v.vid.innerLength() + v.vuser_id.innerLength() + v.vmessage.innerLength() + v.vpts.innerLength() + v.vpts_count.innerLength() + v.vdate.innerLength() + (v.has_fwd_from_id() ? v.vfwd_from_id.innerLength() : 0) + (v.has_fwd_date() ? v.vfwd_date.innerLength() : 0) + (v.has_reply_to_msg_id() ? v.vreply_to_msg_id.innerLength() : 0);
} }
case mtpc_updateShortChatMessage: { case mtpc_updateShortChatMessage: {
const MTPDupdateShortChatMessage &v(c_updateShortChatMessage()); const MTPDupdateShortChatMessage &v(c_updateShortChatMessage());
return v.vid.innerLength() + v.vfrom_id.innerLength() + v.vchat_id.innerLength() + v.vmessage.innerLength() + v.vpts.innerLength() + v.vpts_count.innerLength() + v.vdate.innerLength(); return v.vflags.innerLength() + v.vid.innerLength() + v.vfrom_id.innerLength() + v.vchat_id.innerLength() + v.vmessage.innerLength() + v.vpts.innerLength() + v.vpts_count.innerLength() + v.vdate.innerLength() + (v.has_fwd_from_id() ? v.vfwd_from_id.innerLength() : 0) + (v.has_fwd_date() ? v.vfwd_date.innerLength() : 0) + (v.has_reply_to_msg_id() ? v.vreply_to_msg_id.innerLength() : 0);
} }
case mtpc_updateShort: { case mtpc_updateShort: {
const MTPDupdateShort &v(c_updateShort()); const MTPDupdateShort &v(c_updateShort());
@ -21085,16 +21113,21 @@ inline void MTPupdates::read(const mtpPrime *&from, const mtpPrime *end, mtpType
case mtpc_updateShortMessage: _type = cons; { case mtpc_updateShortMessage: _type = cons; {
if (!data) setData(new MTPDupdateShortMessage()); if (!data) setData(new MTPDupdateShortMessage());
MTPDupdateShortMessage &v(_updateShortMessage()); MTPDupdateShortMessage &v(_updateShortMessage());
v.vflags.read(from, end);
v.vid.read(from, end); v.vid.read(from, end);
v.vfrom_id.read(from, end); v.vuser_id.read(from, end);
v.vmessage.read(from, end); v.vmessage.read(from, end);
v.vpts.read(from, end); v.vpts.read(from, end);
v.vpts_count.read(from, end); v.vpts_count.read(from, end);
v.vdate.read(from, end); v.vdate.read(from, end);
if (v.has_fwd_from_id()) { v.vfwd_from_id.read(from, end); } else { v.vfwd_from_id = MTPint(); }
if (v.has_fwd_date()) { v.vfwd_date.read(from, end); } else { v.vfwd_date = MTPint(); }
if (v.has_reply_to_msg_id()) { v.vreply_to_msg_id.read(from, end); } else { v.vreply_to_msg_id = MTPint(); }
} break; } break;
case mtpc_updateShortChatMessage: _type = cons; { case mtpc_updateShortChatMessage: _type = cons; {
if (!data) setData(new MTPDupdateShortChatMessage()); if (!data) setData(new MTPDupdateShortChatMessage());
MTPDupdateShortChatMessage &v(_updateShortChatMessage()); MTPDupdateShortChatMessage &v(_updateShortChatMessage());
v.vflags.read(from, end);
v.vid.read(from, end); v.vid.read(from, end);
v.vfrom_id.read(from, end); v.vfrom_id.read(from, end);
v.vchat_id.read(from, end); v.vchat_id.read(from, end);
@ -21102,6 +21135,9 @@ inline void MTPupdates::read(const mtpPrime *&from, const mtpPrime *end, mtpType
v.vpts.read(from, end); v.vpts.read(from, end);
v.vpts_count.read(from, end); v.vpts_count.read(from, end);
v.vdate.read(from, end); v.vdate.read(from, end);
if (v.has_fwd_from_id()) { v.vfwd_from_id.read(from, end); } else { v.vfwd_from_id = MTPint(); }
if (v.has_fwd_date()) { v.vfwd_date.read(from, end); } else { v.vfwd_date = MTPint(); }
if (v.has_reply_to_msg_id()) { v.vreply_to_msg_id.read(from, end); } else { v.vreply_to_msg_id = MTPint(); }
} break; } break;
case mtpc_updateShort: _type = cons; { case mtpc_updateShort: _type = cons; {
if (!data) setData(new MTPDupdateShort()); if (!data) setData(new MTPDupdateShort());
@ -21135,15 +21171,20 @@ inline void MTPupdates::write(mtpBuffer &to) const {
switch (_type) { switch (_type) {
case mtpc_updateShortMessage: { case mtpc_updateShortMessage: {
const MTPDupdateShortMessage &v(c_updateShortMessage()); const MTPDupdateShortMessage &v(c_updateShortMessage());
v.vflags.write(to);
v.vid.write(to); v.vid.write(to);
v.vfrom_id.write(to); v.vuser_id.write(to);
v.vmessage.write(to); v.vmessage.write(to);
v.vpts.write(to); v.vpts.write(to);
v.vpts_count.write(to); v.vpts_count.write(to);
v.vdate.write(to); v.vdate.write(to);
if (v.has_fwd_from_id()) v.vfwd_from_id.write(to);
if (v.has_fwd_date()) v.vfwd_date.write(to);
if (v.has_reply_to_msg_id()) v.vreply_to_msg_id.write(to);
} break; } break;
case mtpc_updateShortChatMessage: { case mtpc_updateShortChatMessage: {
const MTPDupdateShortChatMessage &v(c_updateShortChatMessage()); const MTPDupdateShortChatMessage &v(c_updateShortChatMessage());
v.vflags.write(to);
v.vid.write(to); v.vid.write(to);
v.vfrom_id.write(to); v.vfrom_id.write(to);
v.vchat_id.write(to); v.vchat_id.write(to);
@ -21151,6 +21192,9 @@ inline void MTPupdates::write(mtpBuffer &to) const {
v.vpts.write(to); v.vpts.write(to);
v.vpts_count.write(to); v.vpts_count.write(to);
v.vdate.write(to); v.vdate.write(to);
if (v.has_fwd_from_id()) v.vfwd_from_id.write(to);
if (v.has_fwd_date()) v.vfwd_date.write(to);
if (v.has_reply_to_msg_id()) v.vreply_to_msg_id.write(to);
} break; } break;
case mtpc_updateShort: { case mtpc_updateShort: {
const MTPDupdateShort &v(c_updateShort()); const MTPDupdateShort &v(c_updateShort());
@ -21200,11 +21244,11 @@ inline MTPupdates::MTPupdates(MTPDupdates *_data) : mtpDataOwner(_data), _type(m
inline MTPupdates MTP_updatesTooLong() { inline MTPupdates MTP_updatesTooLong() {
return MTPupdates(mtpc_updatesTooLong); return MTPupdates(mtpc_updatesTooLong);
} }
inline MTPupdates MTP_updateShortMessage(MTPint _id, MTPint _from_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date) { inline MTPupdates MTP_updateShortMessage(MTPint _flags, MTPint _id, MTPint _user_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date, MTPint _fwd_from_id, MTPint _fwd_date, MTPint _reply_to_msg_id) {
return MTPupdates(new MTPDupdateShortMessage(_id, _from_id, _message, _pts, _pts_count, _date)); return MTPupdates(new MTPDupdateShortMessage(_flags, _id, _user_id, _message, _pts, _pts_count, _date, _fwd_from_id, _fwd_date, _reply_to_msg_id));
} }
inline MTPupdates MTP_updateShortChatMessage(MTPint _id, MTPint _from_id, MTPint _chat_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date) { inline MTPupdates MTP_updateShortChatMessage(MTPint _flags, MTPint _id, MTPint _from_id, MTPint _chat_id, const MTPstring &_message, MTPint _pts, MTPint _pts_count, MTPint _date, MTPint _fwd_from_id, MTPint _fwd_date, MTPint _reply_to_msg_id) {
return MTPupdates(new MTPDupdateShortChatMessage(_id, _from_id, _chat_id, _message, _pts, _pts_count, _date)); return MTPupdates(new MTPDupdateShortChatMessage(_flags, _id, _from_id, _chat_id, _message, _pts, _pts_count, _date, _fwd_from_id, _fwd_date, _reply_to_msg_id));
} }
inline MTPupdates MTP_updateShort(const MTPUpdate &_update, MTPint _date) { inline MTPupdates MTP_updateShort(const MTPUpdate &_update, MTPint _date) {
return MTPupdates(new MTPDupdateShort(_update, _date)); return MTPupdates(new MTPDupdateShort(_update, _date));

View file

@ -351,8 +351,8 @@ updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Ve
updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference; updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference;
updatesTooLong#e317af7e = Updates; updatesTooLong#e317af7e = Updates;
updateShortMessage#b87da3b1 id:int from_id:int message:string pts:int pts_count:int date:int = Updates; updateShortMessage#ed5c2127 flags:# id:int user_id:int message:string pts:int pts_count:int date:int fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int = Updates;
updateShortChatMessage#20e85ded id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int = Updates; updateShortChatMessage#52238b3c flags:# id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int = Updates;
updateShort#78d4dec1 update:Update date:int = Updates; updateShort#78d4dec1 update:Update date:int = Updates;
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates; updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates; updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
@ -561,9 +561,9 @@ contactLinkContact#d502c2d0 = ContactLink;
---functions--- ---functions---
invokeAfterMsg#cb9f372d msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X; invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone; auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone;
auth.sendCode#768d5f4d phone_number:string sms_type:int api_id:int api_hash:string lang_code:string = auth.SentCode; auth.sendCode#768d5f4d phone_number:string sms_type:int api_id:int api_hash:string lang_code:string = auth.SentCode;
@ -668,7 +668,7 @@ messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
initConnection#69796de9 api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X; initConnection#69796de9 {X:Type} api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X;
help.getSupport#9cdf08cd = help.Support; help.getSupport#9cdf08cd = help.Support;
@ -687,7 +687,7 @@ account.deleteAccount#418d4e0b reason:string = Bool;
account.getAccountTTL#8fc711d = AccountDaysTTL; account.getAccountTTL#8fc711d = AccountDaysTTL;
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool; account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
invokeWithLayer#da9b0d0d layer:int query:!X = X; invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
contacts.resolveUsername#bf0131c username:string = User; contacts.resolveUsername#bf0131c username:string = User;

View file

@ -1876,7 +1876,7 @@ void OverviewWidget::onDeleteSelectedSure() {
} }
if (!ids.isEmpty()) { if (!ids.isEmpty()) {
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(ids))); App::main()->deleteMessages(ids);
} }
onClearSelected(); onClearSelected();
@ -1896,7 +1896,7 @@ void OverviewWidget::onDeleteContextSure() {
} }
if (item->id > 0) { if (item->id > 0) {
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(1, MTP_int(item->id)))); App::main()->deleteMessages(QVector<MTPint>(1, MTP_int(item->id)));
} }
item->destroy(); item->destroy();
if (App::main() && App::main()->peer() == peer()) { if (App::main() && App::main()->peer() == peer()) {

View file

@ -381,11 +381,8 @@ void ProfileInner::reorderParticipants() {
bool ProfileInner::event(QEvent *e) { bool ProfileInner::event(QEvent *e) {
if (e->type() == QEvent::MouseMove) { if (e->type() == QEvent::MouseMove) {
QMouseEvent *ev = dynamic_cast<QMouseEvent*>(e); _lastPos = static_cast<QMouseEvent*>(e)->globalPos();
if (ev) { updateSelected();
_lastPos = ev->globalPos();
updateSelected();
}
} }
return QWidget::event(e); return QWidget::event(e);
} }

View file

@ -484,6 +484,8 @@ void Window::clearPasscode() {
_passcode = 0; _passcode = 0;
if (intro) { if (intro) {
intro->animShow(bg, true); intro->animShow(bg, true);
} else if (settings) {
settings->animShow(bg, true);
} else { } else {
main->animShow(bg, true); main->animShow(bg, true);
} }
@ -1204,16 +1206,33 @@ void Window::quit() {
notifyClearFast(); notifyClearFast();
} }
void Window::notifySchedule(History *history, MsgId msgId) { void Window::notifySchedule(History *history, HistoryItem *item) {
if (App::quiting() || !history->currentNotification() || !main) return; if (App::quiting() || !history->currentNotification() || !main) return;
UserData *notifyByFrom = (history->peer->chat && item->notifyByFrom()) ? item->from() : 0;
bool haveSetting = (history->peer->notify != UnknownNotifySettings); bool haveSetting = (history->peer->notify != UnknownNotifySettings);
if (haveSetting) { if (haveSetting) {
if (history->peer->notify != EmptyNotifySettings && history->peer->notify->mute > unixtime()) { if (history->peer->notify != EmptyNotifySettings && history->peer->notify->mute > unixtime()) {
history->clearNotifications(); if (notifyByFrom) {
return; haveSetting = (item->from()->notify != UnknownNotifySettings);
if (haveSetting) {
if (notifyByFrom->notify != EmptyNotifySettings && notifyByFrom->notify->mute > unixtime()) {
history->popNotification(item);
return;
}
} else {
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(notifyByFrom->input));
}
} else {
history->popNotification(item);
return;
}
} }
} else { } else {
if (notifyByFrom && notifyByFrom->notify == UnknownNotifySettings) {
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(notifyByFrom->input), 10);
}
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(history->peer->input)); App::wnd()->getNotifySetting(MTP_inputNotifyPeer(history->peer->input));
} }
@ -1228,19 +1247,19 @@ void Window::notifySchedule(History *history, MsgId msgId) {
} }
uint64 when = getms(true) + delay; uint64 when = getms(true) + delay;
notifyWhenAlerts[history].insert(when, NullType()); notifyWhenAlerts[history].insert(when, notifyByFrom);
if (cDesktopNotify() && !psSkipDesktopNotify()) { if (cDesktopNotify() && !psSkipDesktopNotify()) {
NotifyWhenMaps::iterator i = notifyWhenMaps.find(history); NotifyWhenMaps::iterator i = notifyWhenMaps.find(history);
if (i == notifyWhenMaps.end()) { if (i == notifyWhenMaps.end()) {
i = notifyWhenMaps.insert(history, NotifyWhenMap()); i = notifyWhenMaps.insert(history, NotifyWhenMap());
} }
if (i.value().constFind(msgId) == i.value().cend()) { if (i.value().constFind(item->id) == i.value().cend()) {
i.value().insert(msgId, when); i.value().insert(item->id, when);
} }
NotifyWaiters *addTo = haveSetting ? &notifyWaiters : &notifySettingWaiters; NotifyWaiters *addTo = haveSetting ? &notifyWaiters : &notifySettingWaiters;
NotifyWaiters::const_iterator it = addTo->constFind(history); NotifyWaiters::const_iterator it = addTo->constFind(history);
if (it == addTo->cend() || it->when > when) { if (it == addTo->cend() || it->when > when) {
addTo->insert(history, NotifyWaiter(msgId, when)); addTo->insert(history, NotifyWaiter(item->id, when, notifyByFrom));
} }
} }
if (haveSetting) { if (haveSetting) {
@ -1300,6 +1319,13 @@ void Window::notifySettingGot() {
} else { } else {
if (history->peer->notify == EmptyNotifySettings || history->peer->notify->mute <= t) { if (history->peer->notify == EmptyNotifySettings || history->peer->notify->mute <= t) {
notifyWaiters.insert(i.key(), i.value()); notifyWaiters.insert(i.key(), i.value());
} else if (UserData *from = i.value().notifyByFrom) {
if (from->notify == UnknownNotifySettings) {
++i;
continue;
} else if (from->notify == EmptyNotifySettings || from->notify->mute <= t) {
notifyWaiters.insert(i.key(), i.value());
}
} }
i = notifySettingWaiters.erase(i); i = notifySettingWaiters.erase(i);
} }
@ -1323,11 +1349,14 @@ void Window::notifyShowNext(NotifyWindow *remove) {
uint64 ms = getms(true), nextAlert = 0; uint64 ms = getms(true), nextAlert = 0;
bool alert = false; bool alert = false;
int32 now = unixtime();
for (NotifyWhenAlerts::iterator i = notifyWhenAlerts.begin(); i != notifyWhenAlerts.end();) { for (NotifyWhenAlerts::iterator i = notifyWhenAlerts.begin(); i != notifyWhenAlerts.end();) {
while (!i.value().isEmpty() && i.value().begin().key() <= ms) { while (!i.value().isEmpty() && i.value().begin().key() <= ms) {
NotifySettingsPtr n = i.key()->peer->notify, f = i.value().begin().value() ? i.value().begin().value()->notify : UnknownNotifySettings;
i.value().erase(i.value().begin()); i.value().erase(i.value().begin());
NotifySettingsPtr n = i.key()->peer->notify; if (n == EmptyNotifySettings || (n != UnknownNotifySettings && n->mute <= now)) {
if (n == EmptyNotifySettings || (n != UnknownNotifySettings && n->mute <= unixtime())) { alert = true;
} else if (f == EmptyNotifySettings || (f != UnknownNotifySettings && f->mute <= now)) { // notify by from()
alert = true; alert = true;
} }
} }

View file

@ -211,7 +211,7 @@ public:
void quit(); void quit();
void notifySettingGot(); void notifySettingGot();
void notifySchedule(History *history, MsgId msgId); void notifySchedule(History *history, HistoryItem *item);
void notifyClear(History *history = 0); void notifyClear(History *history = 0);
void notifyClearFast(); void notifyClearFast();
void notifyShowNext(NotifyWindow *remove = 0); void notifyShowNext(NotifyWindow *remove = 0);
@ -315,17 +315,18 @@ private:
typedef QMap<History*, NotifyWhenMap> NotifyWhenMaps; typedef QMap<History*, NotifyWhenMap> NotifyWhenMaps;
NotifyWhenMaps notifyWhenMaps; NotifyWhenMaps notifyWhenMaps;
struct NotifyWaiter { struct NotifyWaiter {
NotifyWaiter(MsgId msg, uint64 when) : msg(msg), when(when) { NotifyWaiter(MsgId msg, uint64 when, UserData *notifyByFrom) : msg(msg), when(when), notifyByFrom(notifyByFrom) {
} }
MsgId msg; MsgId msg;
uint64 when; uint64 when;
UserData *notifyByFrom;
}; };
typedef QMap<History*, NotifyWaiter> NotifyWaiters; typedef QMap<History*, NotifyWaiter> NotifyWaiters;
NotifyWaiters notifyWaiters; NotifyWaiters notifyWaiters;
NotifyWaiters notifySettingWaiters; NotifyWaiters notifySettingWaiters;
SingleTimer notifyWaitTimer; SingleTimer notifyWaitTimer;
typedef QMap<uint64, NullType> NotifyWhenAlert; typedef QMap<uint64, UserData*> NotifyWhenAlert;
typedef QMap<History*, NotifyWhenAlert> NotifyWhenAlerts; typedef QMap<History*, NotifyWhenAlert> NotifyWhenAlerts;
NotifyWhenAlerts notifyWhenAlerts; NotifyWhenAlerts notifyWhenAlerts;

View file

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

Binary file not shown.

View file

@ -162,6 +162,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_apiwrap.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_application.cpp"> <ClCompile Include="GeneratedFiles\Debug\moc_application.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -412,6 +416,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_apiwrap.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_application.cpp"> <ClCompile Include="GeneratedFiles\Deploy\moc_application.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -671,6 +679,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_apiwrap.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_application.cpp"> <ClCompile Include="GeneratedFiles\Release\moc_application.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@ -906,6 +918,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\style_auto.cpp" /> <ClCompile Include="GeneratedFiles\style_auto.cpp" />
<ClCompile Include="SourceFiles\apiwrap.cpp" />
<ClCompile Include="SourceFiles\app.cpp" /> <ClCompile Include="SourceFiles\app.cpp" />
<ClCompile Include="SourceFiles\application.cpp" /> <ClCompile Include="SourceFiles\application.cpp" />
<ClCompile Include="SourceFiles\audio.cpp" /> <ClCompile Include="SourceFiles\audio.cpp" />
@ -1058,6 +1071,20 @@
<ClInclude Include="GeneratedFiles\style_auto.h" /> <ClInclude Include="GeneratedFiles\style_auto.h" />
<ClInclude Include="GeneratedFiles\style_classes.h" /> <ClInclude Include="GeneratedFiles\style_classes.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<CustomBuild Include="SourceFiles\apiwrap.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing apiwrap.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/apiwrap.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing apiwrap.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/apiwrap.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing apiwrap.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/apiwrap.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
</CustomBuild>
<ClInclude Include="SourceFiles\app.h" /> <ClInclude Include="SourceFiles\app.h" />
<CustomBuild Include="SourceFiles\boxes\aboutbox.h"> <CustomBuild Include="SourceFiles\boxes\aboutbox.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing aboutbox.h...</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing aboutbox.h...</Message>

View file

@ -846,6 +846,18 @@
<ClCompile Include="SourceFiles\boxes\passcodebox.cpp"> <ClCompile Include="SourceFiles\boxes\passcodebox.cpp">
<Filter>boxes</Filter> <Filter>boxes</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="SourceFiles\apiwrap.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_apiwrap.cpp">
<Filter>Generated Files\Deploy</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_apiwrap.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_apiwrap.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h"> <ClInclude Include="SourceFiles\stdafx.h">
@ -1124,6 +1136,9 @@
<CustomBuild Include="SourceFiles\boxes\passcodebox.h"> <CustomBuild Include="SourceFiles\boxes\passcodebox.h">
<Filter>boxes</Filter> <Filter>boxes</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="SourceFiles\apiwrap.h">
<Filter>Source Files</Filter>
</CustomBuild>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="SourceFiles\art\icon256.ico" /> <Image Include="SourceFiles\art\icon256.ico" />

View file

@ -1657,7 +1657,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.20; CURRENT_PROJECT_VERSION = 0.7.21;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
@ -1675,7 +1675,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 0.7.20; CURRENT_PROJECT_VERSION = 0.7.21;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_OPTIMIZATION_LEVEL = fast; GCC_OPTIMIZATION_LEVEL = fast;
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@ -1701,10 +1701,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.20; CURRENT_PROJECT_VERSION = 0.7.21;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 0.7; DYLIB_COMPATIBILITY_VERSION = 0.7;
DYLIB_CURRENT_VERSION = 0.7.20; DYLIB_CURRENT_VERSION = 0.7.21;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = ""; FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@ -1842,10 +1842,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.7.20; CURRENT_PROJECT_VERSION = 0.7.21;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.7; DYLIB_COMPATIBILITY_VERSION = 0.7;
DYLIB_CURRENT_VERSION = 0.7.20; DYLIB_CURRENT_VERSION = 0.7.21;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = ""; FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;

View file

@ -1,2 +1,2 @@
echo 7020 0.7.20 0 echo 7021 0.7.21 1
# AppVersion AppVersionStr DevChannel # AppVersion AppVersionStr DevChannel