diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index e89b8be690..cfc0023038 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -127,6 +127,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_edit_deleted" = "This message was deleted"; "lng_edit_too_long" = "Your message text is too long"; "lng_edit_message" = "Edit message"; +"lng_edit_message_text" = "New message text.."; "lng_deleted" = "Unknown"; "lng_deleted_message" = "Deleted message"; @@ -653,6 +654,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_comment_ph" = "Write a comment.."; "lng_broadcast_ph" = "Broadcast a message.."; "lng_record_cancel" = "Release outside this field to cancel"; +"lng_will_be_notified" = "Members will be notified when you post"; +"lng_wont_be_notified" = "Members will not be notified when you post"; "lng_empty_history" = ""; "lng_willbe_history" = "Please select a chat to start messaging"; "lng_message_with_from" = "[c]{from}:[/c] {message}"; @@ -712,6 +715,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_context_open_link" = "Open Link"; "lng_context_copy_link" = "Copy Link"; +"lng_context_copy_post_link" = "Copy Post Link"; "lng_context_open_email" = "Write to this address"; "lng_context_copy_email" = "Copy email address"; "lng_context_open_hashtag" = "Search by hashtag"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 5638278665..2068c1fe5b 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1429,6 +1429,7 @@ replyTop: 8px; replyBottom: 6px; replyIconPos: point(13px, 13px); replyIcon: sprite(343px, 197px, 24px, 24px); +editIcon: sprite(371px, 286px, 24px, 24px); replyCancel: iconedButton(btnDefIconed) { icon: sprite(165px, 24px, 14px, 14px); iconPos: point(17px, 17px); diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index ce32997180..11d3fbb61b 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 16b454f057..daa7b16f35 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 205e87895b..42473d4699 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org static const int32 AppVersion = 9026; static const wchar_t *AppVersionStr = L"0.9.26"; static const bool DevVersion = false; -//#define BETA_VERSION (9019002ULL) // just comment this line to build public version +#define BETA_VERSION (9026001ULL) // just comment this line to build public version static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index d87fc5b037..98fa3b9f4d 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -280,7 +280,8 @@ History::History(const PeerId &peerId) : width(0), height(0) , oldLoaded(false) , newLoaded(true) , lastMsg(0) -, draftToId(0) +, msgDraft(0) +, editDraft(0) , lastWidth(0) , lastScrollTop(ScrollMax) , lastShowAtMsgId(ShowAtUnreadMsgId) @@ -294,8 +295,7 @@ History::History(const PeerId &peerId) : width(0), height(0) , textCachedFor(0) , lastItemTextCache(st::dlgRichMinWidth) , posInDialogs(0) -, typingText(st::dlgRichMinWidth) -{ +, typingText(st::dlgRichMinWidth) { if (peer->isChannel() || (peer->isUser() && peer->asUser()->botInfo)) { outboxReadBefore = INT_MAX; } @@ -2674,6 +2674,12 @@ void History::removeBlock(HistoryBlock *block) { delete block; } +History::~History() { + clear(); + deleteAndMark(msgDraft); + deleteAndMark(editDraft); +} + int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem) { int32 y = 0; for (Items::iterator i = items.begin(), e = items.end(); i != e; ++i) { @@ -2926,7 +2932,8 @@ bool HistoryItem::canEdit(const QDateTime &cur) const { t != MediaTypeFile && t != MediaTypeGif && t != MediaTypeMusicFile && - t != MediaTypeVoiceFile) { + t != MediaTypeVoiceFile && + t != MediaTypeWebPage) { return false; } } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index d1e410d1fd..6bf69c08f5 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -149,6 +149,42 @@ struct SendAction { int32 progress; }; +struct HistoryDraft { + HistoryDraft() : msgId(0), previewCancelled(false) { + } + HistoryDraft(const QString &text, MsgId msgId, const MessageCursor &cursor, bool previewCancelled) + : text(text) + , msgId(msgId) + , cursor(cursor) + , previewCancelled(previewCancelled) { + } + HistoryDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled) + : text(field.getLastText()) + , msgId(msgId) + , cursor(field) + , previewCancelled(previewCancelled) { + } + QString text; + MsgId msgId; // replyToId for message draft, editMsgId for edit draft + MessageCursor cursor; + bool previewCancelled; +}; +struct HistoryEditDraft : public HistoryDraft { + HistoryEditDraft() + : HistoryDraft() + , saveRequest(0) { + } + HistoryEditDraft(const QString &text, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequest = 0) + : HistoryDraft(text, msgId, cursor, previewCancelled) + , saveRequest(saveRequest) { + } + HistoryEditDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequest = 0) + : HistoryDraft(field, msgId, previewCancelled) + , saveRequest(saveRequest) { + } + mtpRequestId saveRequest; +}; + class HistoryMedia; class HistoryMessage; class HistoryUnreadBar; @@ -184,9 +220,7 @@ public: void blockResized(HistoryBlock *block, int32 dh); void removeBlock(HistoryBlock *block); - virtual ~History() { - clear(); - } + virtual ~History(); HistoryItem *createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction); HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, int32 flags, QDateTime date, int32 from, HistoryMessage *msg); @@ -284,10 +318,20 @@ public: typedef QList NotifyQueue; NotifyQueue notifies; - QString draft; - MsgId draftToId; - MessageCursor draftCursor; - bool draftPreviewCancelled; + HistoryDraft *msgDraft; + HistoryEditDraft *editDraft; + HistoryDraft *draft() { + return editDraft ? editDraft : msgDraft; + } + void setMsgDraft(HistoryDraft *draft) { + if (msgDraft) delete msgDraft; + msgDraft = draft; + } + void setEditDraft(HistoryEditDraft *draft) { + if (editDraft) delete editDraft; + editDraft = draft; + } + int32 lastWidth, lastScrollTop; MsgId lastShowAtMsgId; bool mute; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index bba4594afa..b21d9f3cd5 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2609,9 +2609,11 @@ void CollapseButton::paintEvent(QPaintEvent *e) { HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _replyToId(0) -, _replyTo(0) , _replyToNameVersion(0) -, _replyForwardPreviewCancel(this, st::replyCancel) +, _editMsgId(0) +, _replyEditMsg(0) +, _fieldBarCancel(this, st::replyCancel) +, _saveEditMsgRequestId(0) , _reportSpamStatus(dbiprsUnknown) , _previewData(0) , _previewRequest(0) @@ -2661,7 +2663,10 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _field(this, st::taMsgField, lang(lng_message_ph)) , _a_record(animation(this, &HistoryWidget::step_record)) , _a_recording(animation(this, &HistoryWidget::step_recording)) -, _recording(false), _inRecord(false), _inField(false), _inReply(false) +, _recording(false) +, _inRecord(false) +, _inField(false) +, _inReplyEdit(false) , a_recordingLevel(0, 0), _recordingSamples(0) , a_recordOver(0, 0), a_recordDown(0, 0), a_recordCancel(st::recordCancel->c, st::recordCancel->c) , _recordCancelWidth(st::recordFont->width(lang(lng_record_cancel))) @@ -2697,7 +2702,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(&_toHistoryEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); connect(&_collapseComments, SIGNAL(clicked()), this, SLOT(onCollapseComments())); - connect(&_replyForwardPreviewCancel, SIGNAL(clicked()), this, SLOT(onReplyForwardPreviewCancel())); + connect(&_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); connect(&_botStart, SIGNAL(clicked()), this, SLOT(onBotStart())); @@ -2745,7 +2750,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed())); connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onCheckMentionDropdown()), Qt::QueuedConnection); - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); _scroll.hide(); _scroll.move(0, 0); @@ -2897,20 +2902,20 @@ void HistoryWidget::onTextChange() { updateStickersByEmoji(); if (_peer && (!_peer->isChannel() || _peer->isMegagroup() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { - if (!_inlineBot && (_textUpdateEventsFlags & TextUpdateEventsSendTyping)) { + if (!_inlineBot && !_editMsgId && (_textUpdateEventsFlags & TextUpdateEventsSendTyping)) { updateSendAction(_history, SendActionTyping); } } if (cHasAudioCapture()) { - if (!_field.hasSendText() && !readyToForward()) { + if (!_field.hasSendText() && !readyToForward() && !_editMsgId) { _previewCancelled = false; _send.hide(); - setMouseTracking(true); + updateMouseTracking(); mouseMoveEvent(0); } else if (!_field.isHidden() && _send.isHidden()) { _send.show(); - setMouseTracking(false); + updateMouseTracking(); _a_record.stop(); _inRecord = _inField = false; a_recordOver = a_recordDown = anim::fvalue(0, 0); @@ -2931,7 +2936,9 @@ void HistoryWidget::onTextChange() { void HistoryWidget::onDraftSaveDelayed() { if (!_peer || !(_textUpdateEventsFlags & TextUpdateEventsSaveDraft)) return; if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) { - if (!Local::hasDraftPositions(_peer->id)) return; + if (!Local::hasDraftCursors(_peer->id)) { + return; + } } onDraftSave(true); } @@ -2947,30 +2954,77 @@ void HistoryWidget::onDraftSave(bool delayed) { return _saveDraftTimer.start(SaveDraftTimeout); } } - writeDraft(); + writeDrafts(Nil, Nil); } -void HistoryWidget::writeDraft(MsgId *replyTo, const QString *text, const MessageCursor *cursor, bool *previewCancelled) { +void HistoryWidget::writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft) { + if (!msgDraft && _editMsgId) msgDraft = &_history->msgDraft; + bool save = _peer && (_saveDraftStart > 0); _saveDraftStart = 0; _saveDraftTimer.stop(); if (_saveDraftText) { if (save) { - Local::writeDraft(_peer->id, Local::MessageDraft(replyTo ? (*replyTo) : _replyToId, text ? (*text) : _field.getLastText(), previewCancelled ? (*previewCancelled) : _previewCancelled)); + Local::MessageDraft localMsgDraft, localEditDraft; + if (msgDraft) { + if (*msgDraft) { + localMsgDraft = Local::MessageDraft((*msgDraft)->msgId, (*msgDraft)->text, (*msgDraft)->previewCancelled); + } + } else { + localMsgDraft = Local::MessageDraft(_replyToId, _field.getLastText(), _previewCancelled); + } + if (editDraft) { + if (*editDraft) { + localEditDraft = Local::MessageDraft((*editDraft)->msgId, (*editDraft)->text, (*editDraft)->previewCancelled); + } + } else if (_editMsgId) { + localEditDraft = Local::MessageDraft(_editMsgId, _field.getLastText(), _previewCancelled); + } + Local::writeDrafts(_peer->id, localMsgDraft, localEditDraft); if (_migrated) { - Local::writeDraft(_migrated->peer->id, Local::MessageDraft()); + Local::writeDrafts(_migrated->peer->id, Local::MessageDraft(), Local::MessageDraft()); } } _saveDraftText = false; } if (save) { - Local::writeDraftPositions(_peer->id, cursor ? (*cursor) : MessageCursor(_field)); + MessageCursor msgCursor, editCursor; + if (msgDraft) { + if (*msgDraft) { + msgCursor = (*msgDraft)->cursor; + } + } else { + msgCursor = MessageCursor(_field); + } + if (editDraft) { + if (*editDraft) { + editCursor = (*editDraft)->cursor; + } + } else if (_editMsgId) { + editCursor = MessageCursor(_field); + } + Local::writeDraftCursors(_peer->id, msgCursor, editCursor); if (_migrated) { - Local::writeDraftPositions(_migrated->peer->id, MessageCursor()); + Local::writeDraftCursors(_migrated->peer->id, MessageCursor(), MessageCursor()); } } } +void HistoryWidget::writeDrafts(History *history) { + Local::MessageDraft localMsgDraft, localEditDraft; + MessageCursor msgCursor, editCursor; + if (history->msgDraft) { + localMsgDraft = Local::MessageDraft(history->msgDraft->msgId, history->msgDraft->text, history->msgDraft->previewCancelled); + msgCursor = history->msgDraft->cursor; + } + if (history->editDraft) { + localEditDraft = Local::MessageDraft(history->editDraft->msgId, history->editDraft->text, history->editDraft->previewCancelled); + editCursor = history->editDraft->cursor; + } + Local::writeDrafts(history->peer->id, localMsgDraft, localEditDraft); + Local::writeDraftCursors(history->peer->id, msgCursor, editCursor); +} + void HistoryWidget::cancelSendAction(History *history, SendActionType type) { QMap, mtpRequestId>::iterator i = _sendActionRequests.find(qMakePair(history, type)); if (i != _sendActionRequests.cend()) { @@ -3374,16 +3428,36 @@ void HistoryWidget::fastShowAtEnd(History *h) { } void HistoryWidget::applyDraft(bool parseLinks) { - if (!_history) return; - setFieldText(_history->draft); - _field.setFocus(); + HistoryDraft *draft = _history ? _history->draft() : 0; + if (!draft) { + setFieldText(QString()); + _field.setFocus(); + _editMsgId = _replyToId = 0; + return; + } + _textUpdateEventsFlags = 0; - _history->draftCursor.applyTo(_field); + setFieldText(draft->text); + _field.setFocus(); + draft->cursor.applyTo(_field); _textUpdateEventsFlags = TextUpdateEventsSaveDraft | TextUpdateEventsSendTyping; - _previewCancelled = _history->draftPreviewCancelled; + _previewCancelled = draft->previewCancelled; + if (_history->editDraft) { + _editMsgId = _history->editDraft->msgId; + _replyToId = 0; + } else { + _editMsgId = 0; + _replyToId = readyToForward() ? 0 : _history->msgDraft->msgId; + } if (parseLinks) { onPreviewParse(); } + if (_editMsgId || _replyToId) { + updateReplyEditTexts(); + if (!_replyEditMsg && App::api()) { + App::api()->requestReplyTo(0, _peer->asChannel(), _editMsgId ? _editMsgId : _replyToId); + } + } } void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool reload) { @@ -3441,13 +3515,22 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re clearAllLoadRequests(); if (_history) { - _history->draft = _field.getLastText(); - if (_migrated) _migrated->draft = QString(); // use migrated draft only once - _history->draftCursor.fillFrom(_field); - _history->draftToId = _replyToId; - _history->draftPreviewCancelled = _previewCancelled; + if (_editMsgId) { + _history->setEditDraft(new HistoryEditDraft(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId)); + } else { + if (_replyToId || !_field.getLastText().isEmpty()) { + _history->setMsgDraft(new HistoryDraft(_field, _replyToId, _previewCancelled)); + } else { + _history->setMsgDraft(Nil); + } + _history->setEditDraft(Nil); + } + if (_migrated) { + _migrated->setMsgDraft(Nil); // use migrated draft only once + _migrated->setEditDraft(Nil); + } - writeDraft(&_history->draftToId, &_history->draft, &_history->draftCursor, &_history->draftPreviewCancelled); + writeDrafts(&_history->msgDraft, &_history->editDraft); if (_scroll.scrollTop() + 1 <= _scroll.scrollTopMax()) { _history->lastWidth = _list->width(); @@ -3467,16 +3550,14 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re updateBotKeyboard(); } - if (_replyToId) { - _replyTo = 0; - _replyToId = 0; - _replyForwardPreviewCancel.hide(); - } - if (_previewData && _previewData->pendingTill >= 0) { - _previewData = 0; - _replyForwardPreviewCancel.hide(); - } + _editMsgId = 0; + _saveEditMsgRequestId = 0; + _replyToId = 0; + _replyEditMsg = 0; + _previewData = 0; _previewCache.clear(); + _fieldBarCancel.hide(); + if (_list) _list->deleteLater(); _list = 0; _scroll.takeWidget(); @@ -3552,39 +3633,20 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re App::main()->peerUpdated(_peer); - if (_history->draftToId > 0 || !_history->draft.isEmpty()) { - applyDraft(false); - _replyToId = readyToForward() ? 0 : _history->draftToId; - } else if (_migrated && !_migrated->draft.isEmpty()) { - _history->draft = _migrated->draft; - _history->draftCursor = _migrated->draftCursor; - _history->draftPreviewCancelled = _migrated->draftPreviewCancelled; - _history->draftToId = 0; - _migrated->draft = QString(); // use migrated draft only once - applyDraft(false); - _replyToId = 0; - } else { - bool fromMigrated = false; - Local::MessageDraft draft = Local::readDraft(_peer->id); - if (draft.replyTo <= 0 && draft.text.isEmpty() && _migrated) { - fromMigrated = true; - draft = Local::readDraft(_migrated->peer->id); + Local::readDraftsWithCursors(_history); + if (_migrated) { + Local::readDraftsWithCursors(_migrated); + _migrated->setEditDraft(Nil); + if (_migrated->msgDraft && !_migrated->msgDraft->text.isEmpty()) { + _migrated->msgDraft->msgId = 0; // edit and reply to drafts can't migrate + if (!_history->msgDraft) { + _history->setMsgDraft(new HistoryDraft(*_migrated->msgDraft)); + } } - setFieldText(draft.text); - _field.setFocus(); - if (!draft.text.isEmpty()) { - MessageCursor cur = Local::readDraftPositions(fromMigrated ? _migrated->peer->id : _peer->id); - _textUpdateEventsFlags = 0; - cur.applyTo(_field); - _textUpdateEventsFlags = TextUpdateEventsSaveDraft | TextUpdateEventsSendTyping; - } - _replyToId = readyToForward() ? 0 : draft.replyTo; - _previewCancelled = draft.previewCancelled; - } - if (_replyToId) { - updateReplyTo(); - if (!_replyTo && App::api()) App::api()->requestReplyTo(0, _peer->asChannel(), _replyToId); + _migrated->setMsgDraft(Nil); } + applyDraft(false); + resizeEvent(0); if (!_previewCancelled) { onPreviewParse(); @@ -3755,7 +3817,7 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute.hide(); _attachMention.hide(); _field.hide(); - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); _attachDocument.hide(); _attachPhoto.hide(); _attachEmoji.hide(); @@ -3812,7 +3874,7 @@ void HistoryWidget::updateControlsVisibility() { _attachPhoto.hide(); _broadcast.hide(); _kbScroll.hide(); - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); _attachDocument.hide(); _attachPhoto.hide(); _attachEmoji.hide(); @@ -3849,7 +3911,7 @@ void HistoryWidget::updateControlsVisibility() { _attachPhoto.hide(); _broadcast.hide(); _kbScroll.hide(); - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); } else { _unblock.hide(); _botStart.hide(); @@ -3857,11 +3919,9 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute.hide(); if (cHasAudioCapture() && !_field.hasSendText() && !readyToForward()) { _send.hide(); - setMouseTracking(true); mouseMoveEvent(0); } else { _send.show(); - setMouseTracking(false); _a_record.stop(); _inRecord = _inField = false; a_recordOver = anim::fvalue(0, 0); @@ -3924,14 +3984,14 @@ void HistoryWidget::updateControlsVisibility() { } updateFieldPlaceholder(); } - if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { - if (_replyForwardPreviewCancel.isHidden()) { - _replyForwardPreviewCancel.show(); + if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { + if (_fieldBarCancel.isHidden()) { + _fieldBarCancel.show(); resizeEvent(0); update(); } } else { - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); } } } else { @@ -3945,7 +4005,7 @@ void HistoryWidget::updateControlsVisibility() { _attachPhoto.hide(); _broadcast.hide(); _kbScroll.hide(); - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); _attachDocument.hide(); _attachPhoto.hide(); _attachEmoji.hide(); @@ -3961,6 +4021,12 @@ void HistoryWidget::updateControlsVisibility() { update(); } } + updateMouseTracking(); +} + +void HistoryWidget::updateMouseTracking() { + bool trackMouse = !_fieldBarCancel.isHidden() || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden()); + setMouseTracking(trackMouse); } void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) { @@ -4432,12 +4498,84 @@ void HistoryWidget::onCollapseComments() { showHistory(_peer->id, switchAt); } +void HistoryWidget::saveEditMsg() { + if (_saveEditMsgRequestId) return; + + WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0); + + EntitiesInText sendingEntities, leftEntities; + QString sendingText, leftText = prepareTextWithEntities(_field.getLastText(), leftEntities, itemTextOptions(_history, App::self()).flags); + + if (!textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { + _field.selectAll(); + _field.setFocus(); + return; + } else if (!leftText.isEmpty()) { + Ui::showLayer(new InformBox(lang(lng_edit_too_long))); + return; + } + + int32 sendFlags = 0; + if (webPageId == CancelledWebPageId) { + sendFlags |= MTPmessages_SendMessage::flag_no_webpage; + } + MTPVector localEntities = linksToMTP(sendingEntities), sentEntities = linksToMTP(sendingEntities, true); + if (!sentEntities.c_vector().v.isEmpty()) { + sendFlags |= MTPmessages_SendMessage::flag_entities; + } + _saveEditMsgRequestId = MTP::send(MTPchannels_EditMessage(MTP_int(sendFlags), _history->peer->asChannel()->inputChannel, MTP_int(_editMsgId), MTP_string(sendingText), sentEntities), rpcDone(&HistoryWidget::saveEditMsgDone, _history), rpcFail(&HistoryWidget::saveEditMsgFail, _history)); +} + +void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates, mtpRequestId req) { + if (App::main()) { + App::main()->sentUpdatesReceived(updates); + } + if (req == _saveEditMsgRequestId) { + _saveEditMsgRequestId = 0; + cancelEdit(); + } + if (history->editDraft && history->editDraft->saveRequest == req) { + history->setEditDraft(Nil); + writeDrafts(history); + } +} + +bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtpRequestId req) { + if (mtpIsFlood(error)) return false; + if (req == _saveEditMsgRequestId) { + _saveEditMsgRequestId = 0; + } + if (history->editDraft && history->editDraft->saveRequest == req) { + history->editDraft->saveRequest = 0; + } + + QString err = error.type(); + if (err == qstr("MESSAGE_ID_INVALID") || err == qstr("CHAT_ADMIN_REQUIRED") || err == qstr("MESSAGE_EDIT_TIME_EXPIRED")) { + Ui::showLayer(new InformBox(lang(lng_edit_error))); + } else if (err == qstr("MESSAGE_NOT_MODIFIED")) { + cancelEdit(); + } else if (err == qstr("MESSAGE_EMPTY")) { + _field.selectAll(); + _field.setFocus(); + } else { + Ui::showLayer(new InformBox(lang(lng_edit_error))); + } + update(); + return true; +} + void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (!_history) return; + if (_editMsgId) { + saveEditMsg(); + return; + } + bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(_channel, replyTo)); WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0); + App::main()->sendMessage(_history, _field.getLastText(), replyTo, _broadcast.checked(), webPageId); setFieldText(QString()); @@ -4658,7 +4796,7 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo _kbHide.hide(); _cmdStart.hide(); _field.hide(); - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); _send.hide(); _unblock.hide(); _botStart.hide(); @@ -4846,7 +4984,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { QPoint pos(e ? e->pos() : mapFromGlobal(QCursor::pos())); bool inRecord = _send.geometry().contains(pos); bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); - bool inReply = QRect(st::replySkip, _field.y() - st::sendPadding - st::replyHeight, width() - st::replySkip - _replyForwardPreviewCancel.width(), st::replyHeight).contains(pos) && replyToId(); + bool inReplyEdit = QRect(st::replySkip, _field.y() - st::sendPadding - st::replyHeight, width() - st::replySkip - _fieldBarCancel.width(), st::replyHeight).contains(pos) && (_editMsgId || replyToId()); bool startAnim = false; if (inRecord != _inRecord) { _inRecord = inRecord; @@ -4862,9 +5000,9 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { a_recordCancel.start(_inField ? st::recordCancel->c : st::recordCancelActive->c); startAnim = true; } - if (inReply != _inReply) { - _inReply = inReply; - setCursor(inReply ? style::cur_pointer : style::cur_default); + if (inReplyEdit != _inReplyEdit) { + _inReplyEdit = inReplyEdit; + setCursor(inReplyEdit ? style::cur_pointer : style::cur_default); } if (startAnim) _a_record.start(); } @@ -5204,8 +5342,9 @@ void HistoryWidget::onKbToggle(bool manual) { _field.setMaxHeight(st::maxFieldHeight); _kbReplyTo = 0; - if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { - _replyForwardPreviewCancel.hide(); + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyToId) { + _fieldBarCancel.hide(); + updateMouseTracking(); } } else { if (_history) { @@ -5223,10 +5362,11 @@ void HistoryWidget::onKbToggle(bool manual) { _field.setMaxHeight(st::maxFieldHeight); _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; - if (_kbReplyTo && !_replyToId) { + if (_kbReplyTo && !_editMsgId && !_replyToId) { updateReplyToName(); - _replyToText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); - _replyForwardPreviewCancel.show(); + _replyEditMsgText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); + _fieldBarCancel.show(); + updateMouseTracking(); } if (manual && _history) { _history->lastKeyboardHiddenId = 0; @@ -5241,10 +5381,11 @@ void HistoryWidget::onKbToggle(bool manual) { _field.setMaxHeight(st::maxFieldHeight - maxh); _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; - if (_kbReplyTo && !_replyToId) { + if (_kbReplyTo && !_editMsgId && !_replyToId) { updateReplyToName(); - _replyToText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); - _replyForwardPreviewCancel.show(); + _replyEditMsgText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); + _fieldBarCancel.show(); + updateMouseTracking(); } if (manual && _history) { _history->lastKeyboardHiddenId = 0; @@ -5414,7 +5555,7 @@ void HistoryWidget::onFieldResize() { _kbScroll.setGeometry(0, height() - kbh, width(), kbh); } _field.move(_attachDocument.x() + _attachDocument.width(), height() - kbh - _field.height() - st::sendPadding); - _replyForwardPreviewCancel.move(width() - _replyForwardPreviewCancel.width(), _field.y() - st::sendPadding - _replyForwardPreviewCancel.height()); + _fieldBarCancel.move(width() - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height()); _attachDocument.move(0, height() - kbh - _attachDocument.height()); _attachPhoto.move(_attachDocument.x(), _attachDocument.y()); @@ -5454,12 +5595,18 @@ void HistoryWidget::onCheckMentionDropdown() { } void HistoryWidget::updateFieldPlaceholder() { - if (_inlineBot && _inlineBot != InlineBotLookingUpData) { - _field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); - } else if (hasBroadcastToggle()) { - _field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); + if (_editMsgId) { + _field.setPlaceholder(lang(lng_edit_message_text)); + _send.setText(lang(lng_settings_save)); } else { - _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); + if (_inlineBot && _inlineBot != InlineBotLookingUpData) { + _field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); + } else if (hasBroadcastToggle()) { + _field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); + } else { + _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); + } + _send.setText(lang(lng_send_button)); } } @@ -5896,7 +6043,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _attachDocument.move(0, height() - kbh - _attachDocument.height()); _attachPhoto.move(_attachDocument.x(), _attachDocument.y()); - _replyForwardPreviewCancel.move(width() - _replyForwardPreviewCancel.width(), _field.y() - st::sendPadding - _replyForwardPreviewCancel.height()); + _fieldBarCancel.move(width() - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height()); updateListSize(App::main() ? App::main()->contentScrollAddToY() : 0); bool kbShowShown = _history && !_kbShown && _keyboard.hasMarkup(); @@ -5945,8 +6092,12 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { void HistoryWidget::itemRemoved(HistoryItem *item) { if (_list) _list->itemRemoved(item); - if (item == _replyTo) { - cancelReply(); + if (item == _replyEditMsg) { + if (_editMsgId) { + cancelEdit(); + } else { + cancelReply(); + } } if (item == _replyReturn) { calcNextReplyReturn(); @@ -5980,7 +6131,7 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, if (_canSendMessages) { newScrollHeight -= (_field.height() + 2 * st::sendPadding); } - if (replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { + if (_editMsgId || replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { newScrollHeight -= st::replyHeight; } if (_kbShown) { @@ -6163,22 +6314,22 @@ void HistoryWidget::updateBotKeyboard(History *h) { bool changed = false; bool wasVisible = _kbShown || _kbReplyTo; - if ((_replyToId && !_replyTo) || !_history) { + if ((_replyToId && !_replyEditMsg) || _editMsgId || !_history) { changed = _keyboard.updateMarkup(0); - } else if (_replyTo) { - changed = _keyboard.updateMarkup(_replyTo); + } else if (_replyToId && _replyEditMsg) { + changed = _keyboard.updateMarkup(_replyEditMsg); } else { changed = _keyboard.updateMarkup(_history->lastKeyboardId ? App::histItemById(_channel, _history->lastKeyboardId) : 0); } updateCmdStartShown(); if (!changed) return; - bool hasMarkup = _keyboard.hasMarkup(), forceReply = _keyboard.forceReply() && !_replyTo; + bool hasMarkup = _keyboard.hasMarkup(), forceReply = _keyboard.forceReply() && (!_replyToId || !_replyEditMsg); if (hasMarkup || forceReply) { if (_keyboard.singleUse() && _keyboard.hasMarkup() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) { _history->lastKeyboardHiddenId = _history->lastKeyboardId; } - if (!isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || _replyTo || (!_field.hasSendText() && !kbWasHidden()))) { + if (!isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || (_replyToId && _replyEditMsg) || (!_field.hasSendText() && !kbWasHidden()))) { if (!_a_show.animating()) { if (hasMarkup) { _kbScroll.show(); @@ -6198,8 +6349,9 @@ void HistoryWidget::updateBotKeyboard(History *h) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); - _replyToText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); - _replyForwardPreviewCancel.show(); + _replyEditMsgText.setText(st::msgFont, _kbReplyTo->inDialogsText(), _textDlgOptions); + _fieldBarCancel.show(); + updateMouseTracking(); } } else { if (!_a_show.animating()) { @@ -6213,7 +6365,8 @@ void HistoryWidget::updateBotKeyboard(History *h) { _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); + updateMouseTracking(); } } } else { @@ -6227,8 +6380,9 @@ void HistoryWidget::updateBotKeyboard(History *h) { _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; _kbReplyTo = 0; - if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { - _replyForwardPreviewCancel.hide(); + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId && !_editMsgId) { + _fieldBarCancel.hide(); + updateMouseTracking(); } } resizeEvent(0); @@ -6271,7 +6425,7 @@ void HistoryWidget::updateCollapseCommentsVisibility() { void HistoryWidget::mousePressEvent(QMouseEvent *e) { _replyForwardPressed = QRect(0, _field.y() - st::sendPadding - st::replyHeight, st::replySkip, st::replyHeight).contains(e->pos()); - if (_replyForwardPressed && !_replyForwardPreviewCancel.isHidden()) { + if (_replyForwardPressed && !_fieldBarCancel.isHidden()) { updateField(); } else if (_inRecord && cHasAudioCapture()) { audioCapture()->start(); @@ -6285,8 +6439,8 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { a_recordDown.start(1); a_recordOver.restart(); _a_record.start(); - } else if (_inReply) { - Ui::showPeerHistory(_peer, replyToId()); + } else if (_inReplyEdit) { + Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId()); } } @@ -6615,16 +6769,25 @@ void HistoryWidget::onReplyToMessage() { App::main()->cancelForwarding(); - _replyTo = to; - _replyToId = to->id; - _replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions); + if (_editMsgId) { + if (!_history->msgDraft) { + _history->setMsgDraft(new HistoryDraft(QString(), to->id, MessageCursor(), false)); + } else { + _history->msgDraft->msgId = to->id; + } + } else { + _replyEditMsg = to; + _replyToId = to->id; + _replyEditMsgText.setText(st::msgFont, _replyEditMsg->inDialogsText(), _textDlgOptions); - updateBotKeyboard(); + updateBotKeyboard(); - if (!_field.isHidden()) _replyForwardPreviewCancel.show(); - updateReplyToName(); - resizeEvent(0); - updateField(); + if (!_field.isHidden()) _fieldBarCancel.show(); + updateMouseTracking(); + updateReplyToName(); + resizeEvent(0); + updateField(); + } _saveDraftText = true; _saveDraftStart = getms(); @@ -6642,7 +6805,42 @@ void HistoryWidget::onEditMessage() { Ui::showLayer(box); } else { delete box; - // edit post + + if (_replyToId || !_field.getLastText().isEmpty()) { + _history->setMsgDraft(new HistoryDraft(_field, _replyToId, _previewCancelled)); + } else { + _history->setMsgDraft(Nil); + } + + QString text(textApplyEntities(to->originalText(), to->originalEntities())); + _history->setEditDraft(new HistoryEditDraft(text, to->id, MessageCursor(text.size(), text.size(), QFIXED_MAX), false)); + applyDraft(false); + + _previewData = 0; + if (HistoryMedia *media = to->getMedia()) { + if (media->type() == MediaTypeWebPage) { + _previewData = static_cast(media)->webpage(); + updatePreview(); + } + } + if (!_previewData) { + onPreviewParse(); + } + + updateBotKeyboard(); + + if (!_field.isHidden()) _fieldBarCancel.show(); + updateFieldPlaceholder(); + updateMouseTracking(); + updateReplyToName(); + resizeEvent(0); + updateField(); + + _saveDraftText = true; + _saveDraftStart = getms(); + onDraftSave(); + + _field.setFocus(); } } @@ -6652,37 +6850,77 @@ bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const { } void HistoryWidget::cancelReply(bool lastKeyboardUsed) { + bool wasReply = _replyToId || (_history && _history->msgDraft && _history->msgDraft->msgId); if (_replyToId) { - _replyTo = 0; + _replyEditMsg = 0; _replyToId = 0; mouseMoveEvent(0); if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) { - _replyForwardPreviewCancel.hide(); + _fieldBarCancel.hide(); + updateMouseTracking(); } updateBotKeyboard(); resizeEvent(0); update(); - + } else if (wasReply) { + if (_history->msgDraft->text.isEmpty()) { + _history->setMsgDraft(Nil); + } else { + _history->msgDraft->msgId = 0; + } + } + if (wasReply) { _saveDraftText = true; _saveDraftStart = getms(); onDraftSave(); } - if (_keyboard.singleUse() && _keyboard.forceReply() && lastKeyboardUsed) { + if (!_editMsgId && _keyboard.singleUse() && _keyboard.forceReply() && lastKeyboardUsed) { if (_kbReplyTo) { onKbToggle(false); } } } +void HistoryWidget::cancelEdit() { + if (!_editMsgId) return; + + _editMsgId = 0; + _replyEditMsg = 0; + _history->setEditDraft(Nil); + applyDraft(); + + if (_saveEditMsgRequestId) { + MTP::cancel(_saveEditMsgRequestId); + _saveEditMsgRequestId = 0; + } + + _saveDraftText = true; + _saveDraftStart = getms(); + onDraftSave(); + + mouseMoveEvent(0); + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !replyToId()) { + _fieldBarCancel.hide(); + updateMouseTracking(); + } + + onTextChange(); + updateBotKeyboard(); + updateFieldPlaceholder(); + + resizeEvent(0); + update(); +} + void HistoryWidget::cancelForwarding() { updateControlsVisibility(); resizeEvent(0); update(); } -void HistoryWidget::onReplyForwardPreviewCancel() { +void HistoryWidget::onFieldBarCancel() { _replyForwardPressed = false; if (_previewData && _previewData->pendingTill >= 0) { _previewCancelled = true; @@ -6691,6 +6929,8 @@ void HistoryWidget::onReplyForwardPreviewCancel() { _saveDraftText = true; _saveDraftStart = getms(); onDraftSave(); + } else if (_editMsgId) { + cancelEdit(); } else if (readyToForward()) { App::main()->cancelForwarding(); } else if (_replyToId) { @@ -6717,7 +6957,10 @@ void HistoryWidget::previewCancel() { _previewData = 0; _previewLinks.clear(); updatePreview(); - if (!_replyToId && !readyToForward() && !_kbReplyTo) _replyForwardPreviewCancel.hide(); + if (!_editMsgId && !_replyToId && !readyToForward() && !_kbReplyTo) { + _fieldBarCancel.hide(); + updateMouseTracking(); + } } void HistoryWidget::onPreviewParse() { @@ -6781,7 +7024,8 @@ void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtp void HistoryWidget::updatePreview() { _previewTimer.stop(); if (_previewData && _previewData->pendingTill >= 0) { - _replyForwardPreviewCancel.show(); + _fieldBarCancel.show(); + updateMouseTracking(); if (_previewData->pendingTill) { _previewTitle.setText(st::msgServiceNameFont, lang(lng_preview_loading), _textNameOptions); _previewDescription.setText(st::msgFont, _previewLinks.splitRef(' ').at(0).toString(), _textDlgOptions); @@ -6818,8 +7062,9 @@ void HistoryWidget::updatePreview() { _previewTitle.setText(st::msgServiceNameFont, title, _textNameOptions); _previewDescription.setText(st::msgFont, desc, _textDlgOptions); } - } else if (!readyToForward() && !replyToId()) { - _replyForwardPreviewCancel.hide(); + } else if (!readyToForward() && !replyToId() && !_editMsgId) { + _fieldBarCancel.hide(); + updateMouseTracking(); } resizeEvent(0); update(); @@ -7035,19 +7280,28 @@ void HistoryWidget::updateTopBarSelection() { update(); } -void HistoryWidget::updateReplyTo(bool force) { - if (!_replyToId || _replyTo) return; - _replyTo = App::histItemById(_channel, _replyToId); - if (_replyTo) { - _replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions); +void HistoryWidget::updateReplyEditTexts(bool force) { + if (_replyEditMsg || (!_editMsgId && !_replyToId)) { + return; + } + _replyEditMsg = App::histItemById(_channel, _editMsgId ? _editMsgId : _replyToId); + if (_replyEditMsg) { + _replyEditMsgText.setText(st::msgFont, _replyEditMsg->inDialogsText(), _textDlgOptions); updateBotKeyboard(); - if (!_field.isHidden() || _recording) _replyForwardPreviewCancel.show(); + if (!_field.isHidden() || _recording) { + _fieldBarCancel.show(); + updateMouseTracking(); + } updateReplyToName(); updateField(); } else if (force) { - cancelReply(); + if (_editMsgId) { + cancelEdit(); + } else { + cancelReply(); + } } } @@ -7061,9 +7315,10 @@ void HistoryWidget::updateForwarding(bool force) { } void HistoryWidget::updateReplyToName() { - if (!_replyTo && (_replyToId || !_kbReplyTo)) return; - _replyToName.setText(st::msgServiceNameFont, App::peerName((_replyTo ? _replyTo : _kbReplyTo)->author()), _textNameOptions); - _replyToNameVersion = (_replyTo ? _replyTo : _kbReplyTo)->author()->nameVersion; + if (_editMsgId) return; + if (!_replyEditMsg && (_replyToId || !_kbReplyTo)) return; + _replyToName.setText(st::msgServiceNameFont, App::peerName((_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()), _textNameOptions); + _replyToNameVersion = (_replyEditMsg ? _replyEditMsg : _kbReplyTo)->author()->nameVersion; } void HistoryWidget::updateField() { @@ -7076,9 +7331,9 @@ void HistoryWidget::drawField(Painter &p) { Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; - HistoryItem *drawReplyTo = _replyToId ? _replyTo : _kbReplyTo; - if (_replyToId || (!hasForward && _kbReplyTo)) { - if (drawReplyTo && drawReplyTo->author()->nameVersion > _replyToNameVersion) { + HistoryItem *drawMsgText = (_editMsgId || _replyToId) ? _replyEditMsg : _kbReplyTo; + if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { + if (!_editMsgId && drawMsgText && drawMsgText->author()->nameVersion > _replyToNameVersion) { updateReplyToName(); } backy -= st::replyHeight; @@ -7093,27 +7348,32 @@ void HistoryWidget::drawField(Painter &p) { } bool drawPreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed; p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor->b); - if (_replyToId || (!hasForward && _kbReplyTo)) { + if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { int32 replyLeft = st::replySkip; - p.drawPixmap(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), App::sprite(), st::replyIcon); + p.drawPixmap(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), App::sprite(), _editMsgId ? st::editIcon : st::replyIcon); if (!drawPreview) { - if (drawReplyTo) { - if (drawReplyTo->getMedia() && drawReplyTo->getMedia()->hasReplyPreview()) { - ImagePtr replyPreview = drawReplyTo->getMedia()->replyPreview(); + if (drawMsgText) { + if (drawMsgText->getMedia() && drawMsgText->getMedia()->hasReplyPreview()) { + ImagePtr replyPreview = drawMsgText->getMedia()->replyPreview(); if (!replyPreview->isNull()) { QRect to(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height())); } 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 - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); - p.setPen((((drawReplyTo->toHistoryMessage() && drawReplyTo->toHistoryMessage()->emptyText()) || drawReplyTo->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p); - _replyToText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); + p.setPen(st::replyColor); + if (_editMsgId) { + p.setFont(st::msgServiceNameFont); + p.drawText(replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_edit_message)); + } else { + _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + } + p.setPen((((drawMsgText->toHistoryMessage() && drawMsgText->toHistoryMessage()->emptyText()) || drawMsgText->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p); + _replyEditMsgText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); } else { p.setFont(st::msgDateFont->f); p.setPen(st::msgInDateFg->p); - p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right())); + p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right())); } } } else if (from && text) { @@ -7131,9 +7391,9 @@ void HistoryWidget::drawField(Painter &p) { forwardLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } p.setPen(st::replyColor->p); - from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); + from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); p.setPen((serviceColor ? st::msgInDateFg : st::msgColor)->p); - text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); + text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); } } if (drawPreview) { @@ -7153,9 +7413,9 @@ void HistoryWidget::drawField(Painter &p) { previewLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } p.setPen(st::replyColor->p); - _previewTitle.drawElided(p, previewLeft, backy + st::msgReplyPadding.top(), width() - previewLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); + _previewTitle.drawElided(p, previewLeft, backy + st::msgReplyPadding.top(), width() - previewLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); p.setPen(st::msgColor->p); - _previewDescription.drawElided(p, previewLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - previewLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); + _previewDescription.drawElided(p, previewLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - previewLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index d18f081035..4c82a86312 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -505,9 +505,10 @@ public: void updateScrollColors(); MsgId replyToId() const; - void updateReplyTo(bool force = false); + void updateReplyEditTexts(bool force = false); bool lastForceReplyReplied(const FullMsgId &replyTo = FullMsgId(NoChannel, -1)) const; void cancelReply(bool lastKeyboardUsed = false); + void cancelEdit(); void updateForwarding(bool force = false); void cancelForwarding(); // called by MainWidget @@ -595,7 +596,7 @@ public slots: void onCancel(); void onReplyToMessage(); void onEditMessage(); - void onReplyForwardPreviewCancel(); + void onFieldBarCancel(); void onCancelSendAction(); @@ -689,12 +690,17 @@ public slots: private: MsgId _replyToId; - HistoryItem *_replyTo; - Text _replyToName, _replyToText; + Text _replyToName; int32 _replyToNameVersion; - IconedButton _replyForwardPreviewCancel; void updateReplyToName(); + MsgId _editMsgId; + + HistoryItem *_replyEditMsg; + Text _replyEditMsgText; + + IconedButton _fieldBarCancel; + void sendExistingDocument(DocumentData *doc, const QString &caption); void sendExistingPhoto(PhotoData *photo, const QString &caption); @@ -702,6 +708,13 @@ private: void drawRecordButton(Painter &p); void drawRecording(Painter &p); + void updateMouseTracking(); + + mtpRequestId _saveEditMsgRequestId; + void saveEditMsg(); + void saveEditMsgDone(History *history, const MTPUpdates &updates, mtpRequestId req); + bool saveEditMsgFail(History *history, const RPCError &error, mtpRequestId req); + DBIPeerReportSpamStatus _reportSpamStatus; void updateReportSpamStatus(); @@ -747,7 +760,8 @@ private: void savedGifsGot(const MTPmessages_SavedGifs &gifs); bool savedGifsFailed(const RPCError &error); - void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0, bool *previewCancelled = 0); + void writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft); + void writeDrafts(History *history); void setFieldText(const QString &text, int32 textUpdateEventsFlags = 0, bool clearUndoHistory = true); QStringList getMediasFromMime(const QMimeData *d); @@ -806,7 +820,7 @@ private: bool _cmdStartShown; MessageField _field; Animation _a_record, _a_recording; - bool _recording, _inRecord, _inField, _inReply; + bool _recording, _inRecord, _inField, _inReplyEdit; anim::ivalue a_recordingLevel; int32 _recordingSamples; anim::fvalue a_recordOver, a_recordDown; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 1c0c7f5540..fb5ed2fbda 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -557,7 +557,7 @@ namespace { }; typedef QMap DraftsMap; - DraftsMap _draftsMap, _draftsPositionsMap; + DraftsMap _draftsMap, _draftCursorsMap; typedef QMap DraftsNotReadMap; DraftsNotReadMap _draftsNotReadMap; @@ -1672,7 +1672,7 @@ namespace { } LOG(("App Info: reading encrypted map..")); - DraftsMap draftsMap, draftsPositionsMap; + DraftsMap draftsMap, draftCursorsMap; DraftsNotReadMap draftsNotReadMap; StorageMap imagesMap, stickerImagesMap, audiosMap; qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0; @@ -1701,7 +1701,7 @@ namespace { FileKey key; quint64 p; map.stream >> key >> p; - draftsPositionsMap.insert(p, key); + draftCursorsMap.insert(p, key); } } break; case lskImages: { @@ -1781,7 +1781,7 @@ namespace { } _draftsMap = draftsMap; - _draftsPositionsMap = draftsPositionsMap; + _draftCursorsMap = draftCursorsMap; _draftsNotReadMap = draftsNotReadMap; _imagesMap = imagesMap; @@ -1860,7 +1860,7 @@ namespace { uint32 mapSize = 0; if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2; - if (!_draftsPositionsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsPositionsMap.size() * sizeof(quint64) * 2; + if (!_draftCursorsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftCursorsMap.size() * sizeof(quint64) * 2; if (!_imagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _imagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32)); if (!_stickerImagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickerImagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32)); if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32)); @@ -1880,9 +1880,9 @@ namespace { mapData.stream << quint64(i.value()) << quint64(i.key()); } } - if (!_draftsPositionsMap.isEmpty()) { - mapData.stream << quint32(lskDraftPosition) << quint32(_draftsPositionsMap.size()); - for (DraftsMap::const_iterator i = _draftsPositionsMap.cbegin(), e = _draftsPositionsMap.cend(); i != e; ++i) { + if (!_draftCursorsMap.isEmpty()) { + mapData.stream << quint32(lskDraftPosition) << quint32(_draftCursorsMap.size()); + for (DraftsMap::const_iterator i = _draftCursorsMap.cbegin(), e = _draftCursorsMap.cend(); i != e; ++i) { mapData.stream << quint64(i.value()) << quint64(i.key()); } } @@ -2180,7 +2180,7 @@ namespace Local { _passKeySalt.clear(); // reset passcode, local key _draftsMap.clear(); - _draftsPositionsMap.clear(); + _draftCursorsMap.clear(); _fileLocations.clear(); _fileLocationPairs.clear(); _fileLocationAliases.clear(); @@ -2237,10 +2237,10 @@ namespace Local { return _oldSettingsVersion; } - void writeDraft(const PeerId &peer, const MessageDraft &draft) { + void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft) { if (!_working()) return; - if (draft.replyTo <= 0 && draft.text.isEmpty()) { + if (msgDraft.msgId <= 0 && msgDraft.text.isEmpty() && editDraft.msgId <= 0) { DraftsMap::iterator i = _draftsMap.find(peer); if (i != _draftsMap.cend()) { clearKey(i.value()); @@ -2257,8 +2257,12 @@ namespace Local { _mapChanged = true; _writeMap(WriteMapFast); } - EncryptedDescriptor data(sizeof(quint64) + _stringSize(draft.text) + sizeof(qint32)); - data.stream << quint64(peer) << draft.text << qint32(draft.replyTo) << qint32(draft.previewCancelled ? 1 : 0); + + EncryptedDescriptor data(sizeof(quint64) + _stringSize(msgDraft.text) + 2 * sizeof(qint32) + _stringSize(editDraft.text) + 2 * sizeof(qint32)); + data.stream << quint64(peer); + data.stream << msgDraft.text << qint32(msgDraft.msgId) << qint32(msgDraft.previewCancelled ? 1 : 0); + data.stream << editDraft.text << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0); + FileWriteDescriptor file(i.value()); file.writeEncrypted(data); @@ -2266,75 +2270,123 @@ namespace Local { } } - MessageDraft readDraft(const PeerId &peer) { - if (!_draftsNotReadMap.remove(peer)) return MessageDraft(); + void clearDraftCursors(const PeerId &peer) { + DraftsMap::iterator i = _draftCursorsMap.find(peer); + if (i != _draftCursorsMap.cend()) { + clearKey(i.value()); + _draftCursorsMap.erase(i); + _mapChanged = true; + _writeMap(); + } + } + + void _readDraftCursors(const PeerId &peer, MessageCursor &msgCursor, MessageCursor &editCursor) { + DraftsMap::iterator j = _draftCursorsMap.find(peer); + if (j == _draftCursorsMap.cend()) { + return; + } + + FileReadDescriptor draft; + if (!readEncryptedFile(draft, j.value())) { + clearDraftCursors(peer); + return; + } + quint64 draftPeer; + qint32 msgPosition = 0, msgAnchor = 0, msgScroll = QFIXED_MAX; + qint32 editPosition = 0, editAnchor = 0, editScroll = QFIXED_MAX; + draft.stream >> draftPeer >> msgPosition >> msgAnchor >> msgScroll; + if (!draft.stream.atEnd()) { + draft.stream >> editPosition >> editAnchor >> editScroll; + } + + if (draftPeer != peer) { + clearDraftCursors(peer); + return; + } + + msgCursor = MessageCursor(msgPosition, msgAnchor, msgScroll); + editCursor = MessageCursor(editPosition, editAnchor, editScroll); + } + + void readDraftsWithCursors(History *h) { + PeerId peer = h->peer->id; + if (!_draftsNotReadMap.remove(peer)) { + clearDraftCursors(peer); + return; + } DraftsMap::iterator j = _draftsMap.find(peer); if (j == _draftsMap.cend()) { - return MessageDraft(); + clearDraftCursors(peer); + return; } FileReadDescriptor draft; if (!readEncryptedFile(draft, j.value())) { clearKey(j.value()); _draftsMap.erase(j); - return MessageDraft(); + clearDraftCursors(peer); + return; } - quint64 draftPeer; - QString draftText; - qint32 draftReplyTo = 0, draftPreviewCancelled = 0; - draft.stream >> draftPeer >> draftText; - if (draft.version >= 7021) draft.stream >> draftReplyTo; - if (draft.version >= 8001) draft.stream >> draftPreviewCancelled; - return (draftPeer == peer) ? MessageDraft(MsgId(draftReplyTo), draftText, (draftPreviewCancelled == 1)) : MessageDraft(); + quint64 draftPeer = 0; + QString msgText, editText; + qint32 msgReplyTo = 0, msgPreviewCancelled = 0, editMsgId = 0, editPreviewCancelled = 0; + draft.stream >> draftPeer >> msgText; + if (draft.version >= 7021) { + draft.stream >> msgReplyTo; + if (draft.version >= 8001) { + draft.stream >> msgPreviewCancelled; + if (!draft.stream.atEnd()) { + draft.stream >> editText >> editMsgId >> editPreviewCancelled; + } + } + } + if (draftPeer != peer) { + clearKey(j.value()); + _draftsMap.erase(j); + clearDraftCursors(peer); + return; + } + + MessageCursor msgCursor, editCursor; + _readDraftCursors(peer, msgCursor, editCursor); + + if (msgText.isEmpty() && !msgReplyTo) { + h->setMsgDraft(Nil); + } else { + h->setMsgDraft(new HistoryDraft(msgText, msgReplyTo, msgCursor, msgPreviewCancelled)); + } + if (!editMsgId) { + h->setEditDraft(Nil); + } else { + h->setEditDraft(new HistoryEditDraft(editText, editMsgId, editCursor, editPreviewCancelled)); + } } - void writeDraftPositions(const PeerId &peer, const MessageCursor &cur) { + void writeDraftCursors(const PeerId &peer, const MessageCursor &msgCursor, const MessageCursor &editCursor) { if (!_working()) return; - if (cur.position == 0 && cur.anchor == 0 && cur.scroll == QFIXED_MAX) { - DraftsMap::iterator i = _draftsPositionsMap.find(peer); - if (i != _draftsPositionsMap.cend()) { - clearKey(i.value()); - _draftsPositionsMap.erase(i); - _mapChanged = true; - _writeMap(); - } + if (msgCursor == MessageCursor() && editCursor == MessageCursor()) { + clearDraftCursors(peer); } else { - DraftsMap::const_iterator i = _draftsPositionsMap.constFind(peer); - if (i == _draftsPositionsMap.cend()) { - i = _draftsPositionsMap.insert(peer, genKey()); + DraftsMap::const_iterator i = _draftCursorsMap.constFind(peer); + if (i == _draftCursorsMap.cend()) { + i = _draftCursorsMap.insert(peer, genKey()); _mapChanged = true; _writeMap(WriteMapFast); } + EncryptedDescriptor data(sizeof(quint64) + sizeof(qint32) * 3); - data.stream << quint64(peer) << qint32(cur.position) << qint32(cur.anchor) << qint32(cur.scroll); + data.stream << quint64(peer) << qint32(msgCursor.position) << qint32(msgCursor.anchor) << qint32(msgCursor.scroll); + data.stream << qint32(editCursor.position) << qint32(editCursor.anchor) << qint32(editCursor.scroll); + FileWriteDescriptor file(i.value()); file.writeEncrypted(data); } } - MessageCursor readDraftPositions(const PeerId &peer) { - DraftsMap::iterator j = _draftsPositionsMap.find(peer); - if (j == _draftsPositionsMap.cend()) { - return MessageCursor(); - } - FileReadDescriptor draft; - if (!readEncryptedFile(draft, j.value())) { - clearKey(j.value()); - _draftsPositionsMap.erase(j); - return MessageCursor(); - } - - quint64 draftPeer; - qint32 curPosition, curAnchor, curScroll; - draft.stream >> draftPeer >> curPosition >> curAnchor >> curScroll; - - return (draftPeer == peer) ? MessageCursor(curPosition, curAnchor, curScroll) : MessageCursor(); - } - - bool hasDraftPositions(const PeerId &peer) { - return (_draftsPositionsMap.constFind(peer) != _draftsPositionsMap.cend()); + bool hasDraftCursors(const PeerId &peer) { + return (_draftCursorsMap.constFind(peer) != _draftCursorsMap.cend()); } void writeFileLocation(MediaKey location, const FileLocation &local) { @@ -3795,8 +3847,8 @@ namespace Local { _draftsMap.clear(); _mapChanged = true; } - if (!_draftsPositionsMap.isEmpty()) { - _draftsPositionsMap.clear(); + if (!_draftCursorsMap.isEmpty()) { + _draftCursorsMap.clear(); _mapChanged = true; } if (_locationsKey) { diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 607ec9d0ff..58df7dc7df 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -106,17 +106,16 @@ namespace Local { int32 oldSettingsVersion(); struct MessageDraft { - MessageDraft(MsgId replyTo = 0, QString text = QString(), bool previewCancelled = false) : replyTo(replyTo), text(text), previewCancelled(previewCancelled) { + MessageDraft(MsgId msgId = 0, QString text = QString(), bool previewCancelled = false) : msgId(msgId), text(text), previewCancelled(previewCancelled) { } - MsgId replyTo; + MsgId msgId; QString text; bool previewCancelled; }; - void writeDraft(const PeerId &peer, const MessageDraft &draft); - MessageDraft readDraft(const PeerId &peer); - void writeDraftPositions(const PeerId &peer, const MessageCursor &cur); - MessageCursor readDraftPositions(const PeerId &peer); - bool hasDraftPositions(const PeerId &peer); + void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft); + void readDraftsWithCursors(History *h); + void writeDraftCursors(const PeerId &peer, const MessageCursor &msgCursor, const MessageCursor &editCursor); + bool hasDraftCursors(const PeerId &peer); void writeFileLocation(MediaKey location, const FileLocation &local); FileLocation readFileLocation(MediaKey location, bool check = true); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 05d65d9a72..349568c2ba 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -516,10 +516,8 @@ bool MainWidget::onShareUrl(const PeerId &peer, const QString &url, const QStrin return false; } History *h = App::history(peer); - h->draft = url + '\n' + text; - h->draftCursor.anchor = url.size() + 1; - h->draftCursor.position = h->draftCursor.anchor + text.size(); - h->draftPreviewCancelled = false; + h->setMsgDraft(new HistoryDraft(url + '\n' + text, 0, MessageCursor(url.size() + 1, url.size() + 1 + text.size(), QFIXED_MAX), false)); + h->setEditDraft(Nil); bool opened = history.peer() && (history.peer()->id == peer); if (opened) { history.applyDraft(); @@ -2039,7 +2037,7 @@ ApiWrap *MainWidget::api() { } void MainWidget::updateReplyTo() { - history.updateReplyTo(true); + history.updateReplyEditTexts(true); } void MainWidget::updateBotKeyboard(History *h) { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 16df19fcd5..86dd227bd0 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1217,3 +1217,7 @@ struct MessageCursor { } int position, anchor, scroll; }; + +inline bool operator==(const MessageCursor &a, const MessageCursor &b) { + return (a.position == b.position) && (a.anchor == b.anchor) && (a.scroll == b.scroll); +} diff --git a/Telegram/SourceFiles/types.cpp b/Telegram/SourceFiles/types.cpp index ce77262ae4..0b16ead7f2 100644 --- a/Telegram/SourceFiles/types.cpp +++ b/Telegram/SourceFiles/types.cpp @@ -35,6 +35,8 @@ uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 }; // Base types compile-time check +NilPointer Nil; + namespace { template class _TypeSizeCheckerHelper { diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 97a1fe7423..00ef2c51f0 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -36,6 +36,22 @@ T *exchange(T *&ptr) { struct NullType { }; +class NilPointer { +public: + template + operator T*() const { + return 0; + } + template + operator T C::*() const { + return 0; + } + +private: + void operator&() const; +}; +extern NilPointer Nil; + template class OrderedSet : public QMap { public: diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index af8188834c..878e146e64 100644 --- a/Telegram/Telegram.rc +++ b/Telegram/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "SourceFiles\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,26,0 - PRODUCTVERSION 0,9,26,0 + FILEVERSION 0,9,26,1 + PRODUCTVERSION 0,9,26,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.9.26.0" + VALUE "FileVersion", "0.9.26.1" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.9.26.0" + VALUE "ProductVersion", "0.9.26.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 30dc689c9b..c7f7c5580f 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1739,7 +1739,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.9.25; + CURRENT_PROJECT_VERSION = 0.9.26; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1768,10 +1768,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.9.25; + CURRENT_PROJECT_VERSION = 0.9.26; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 0.9; - DYLIB_CURRENT_VERSION = 0.9.25; + DYLIB_CURRENT_VERSION = 0.9.26; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1909,10 +1909,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.9.25; + CURRENT_PROJECT_VERSION = 0.9.26; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.9; - DYLIB_CURRENT_VERSION = 0.9.25; + DYLIB_CURRENT_VERSION = 0.9.26; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ""; diff --git a/Telegram/Version b/Telegram/Version index bc8bffbaf8..7e97206f14 100644 --- a/Telegram/Version +++ b/Telegram/Version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.9 AppVersionStrSmall 0.9.26 AppVersionStr 0.9.26 DevChannel 0 -BetaVersion 0 9019002 +BetaVersion 9026001