From 54d5b6dd716d04436bfec80b6e159c935dfe2037 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Sep 2015 10:52:37 +0300 Subject: [PATCH] forward/delete for channels, prepared load unread by offset_id, not done yet! --- Telegram/Resources/lang.strings | 1 + Telegram/SourceFiles/app.cpp | 16 ++-- Telegram/SourceFiles/app.h | 3 +- Telegram/SourceFiles/dialogswidget.cpp | 28 +++++- Telegram/SourceFiles/history.cpp | 14 +-- Telegram/SourceFiles/history.h | 6 +- Telegram/SourceFiles/historywidget.cpp | 121 +++++++++++++++--------- Telegram/SourceFiles/historywidget.h | 16 ++++ Telegram/SourceFiles/mainwidget.cpp | 40 ++++---- Telegram/SourceFiles/mainwidget.h | 6 +- Telegram/SourceFiles/mediaview.cpp | 13 ++- Telegram/SourceFiles/mediaview.h | 1 + Telegram/SourceFiles/overviewwidget.cpp | 32 +++++-- Telegram/SourceFiles/pspecific_wnd.cpp | 15 +-- 14 files changed, 205 insertions(+), 107 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 033506413b..be5ffa2698 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -584,6 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_send_image_compressed" = "Send compressed image"; "lng_forward_choose" = "Choose recipient.."; +"lng_forward_cant" = "Sorry, no way to forward here :("; "lng_forward_confirm" = "Forward to {recipient}?"; "lng_forward_share_contact" = "Share contact to {recipient}?"; "lng_forward_send_file_confirm" = "Send «{name}» to {recipient}?"; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index f0549bb30a..5454a47f4c 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1580,16 +1580,20 @@ namespace App { return ::histories; } - History *history(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead) { + History *history(const PeerId &peer) { + Histories::const_iterator i = ::histories.constFind(peer); + if (i == ::histories.cend()) { + i = App::histories().insert(peer, new History(peer)); + } + return i.value(); + } + + History *historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead) { Histories::const_iterator i = ::histories.constFind(peer); if (i == ::histories.cend()) { i = App::histories().insert(peer, new History(peer)); i.value()->setUnreadCount(unreadCnt, false); - if (maxInboxRead) { - i.value()->inboxReadTill = maxInboxRead; - } - } else if (maxInboxRead) { - i.value()->inboxReadTill = qMax(i.value()->inboxReadTill, maxInboxRead); + i.value()->inboxReadBefore = maxInboxRead + 1; } return i.value(); } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 0c6267aee5..e2370ced4a 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -166,7 +166,8 @@ namespace App { MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo); Histories &histories(); - History *history(const PeerId &peer, int32 unreadCnt = 0, int32 maxInboxRead = 0); + History *history(const PeerId &peer); + History *historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead); History *historyLoaded(const PeerId &peer); HistoryItem *histItemById(ChannelId channelId, MsgId itemId); inline HistoryItem *histItemById(const FullMsgId &msgId) { diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 3f79971faa..c5e4392058 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -761,11 +761,29 @@ void DialogsListWidget::itemRemoved(HistoryItem *item) { void DialogsListWidget::dialogsReceived(const QVector &added) { for (QVector::const_iterator i = added.cbegin(), e = added.cend(); i != e; ++i) { + History *history = 0; switch (i->type()) { - case mtpc_dialog: addDialog(i->c_dialog()); break; - case mtpc_dialogChannel: addDialogChannel(i->c_dialogChannel()); break; + case mtpc_dialog: { + const MTPDdialog &d(i->c_dialog()); + history = App::historyFromDialog(peerFromMTP(d.vpeer), d.vunread_count.v, d.vread_inbox_max_id.v); + App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, history); + } break; + + case mtpc_dialogChannel: { + const MTPDdialogChannel &d(i->c_dialogChannel()); + History *history = App::historyFromDialog(peerFromMTP(d.vpeer), d.vunread_important_count.v, d.vread_inbox_max_id.v); + App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, history); + } break; + } + + if (history) { + if (!history->lastMsgDate.isNull()) addSavedPeersAfter(history->lastMsgDate); + History::DialogLinks links = dialogs.addToEnd(history); + history->dialogs = links; + contactsNoDialogs.del(history->peer); } } + if (App::wnd()) App::wnd()->updateCounter(); if (!sel && dialogs.list.count) { sel = dialogs.list.begin; @@ -948,7 +966,7 @@ void DialogsListWidget::clearFilter() { } void DialogsListWidget::addDialog(const MTPDdialog &dialog) { - History *history = App::history(peerFromMTP(dialog.vpeer), dialog.vunread_count.v, dialog.vread_inbox_max_id.v); + History *history = App::historyFromDialog(peerFromMTP(dialog.vpeer), dialog.vunread_count.v, dialog.vread_inbox_max_id.v); if (!history->lastMsgDate.isNull()) addSavedPeersAfter(history->lastMsgDate); History::DialogLinks links = dialogs.addToEnd(history); @@ -959,7 +977,7 @@ void DialogsListWidget::addDialog(const MTPDdialog &dialog) { } void DialogsListWidget::addDialogChannel(const MTPDdialogChannel &dialogChannel) { - History *history = App::history(peerFromMTP(dialogChannel.vpeer), dialogChannel.vunread_important_count.v, dialogChannel.vread_inbox_max_id.v); + History *history = App::historyFromDialog(peerFromMTP(dialogChannel.vpeer), dialogChannel.vunread_important_count.v, dialogChannel.vread_inbox_max_id.v); if (!history->lastMsgDate.isNull()) addSavedPeersAfter(history->lastMsgDate); History::DialogLinks links = dialogs.addToEnd(history); @@ -1603,6 +1621,7 @@ void DialogsWidget::unreadCountsReceived(const QVector &dialogs) { App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, j.value()); if (d.vunread_count.v >= j.value()->unreadCount) { j.value()->setUnreadCount(d.vunread_count.v, false); + j.value()->inboxReadBefore = d.vread_inbox_max_id.v + 1; } } } break; @@ -1613,6 +1632,7 @@ void DialogsWidget::unreadCountsReceived(const QVector &dialogs) { App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, j.value()); if (d.vunread_important_count.v >= j.value()->unreadCount) { j.value()->setUnreadCount(d.vunread_important_count.v, false); + j.value()->inboxReadBefore = d.vread_inbox_max_id.v + 1; } } } break; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 2579b9ee79..2b03c177ef 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -303,8 +303,8 @@ void FakeDialogRow::paint(QPainter &p, int32 w, bool act, bool sel) const { History::History(const PeerId &peerId) : width(0), height(0) , msgCount(0) , unreadCount(0) -, inboxReadTill(0) -, outboxReadTill(0) +, inboxReadBefore(1) +, outboxReadBefore(1) , showFrom(0) , unreadBar(0) , peer(App::peer(peerId)) @@ -1321,7 +1321,7 @@ MsgId History::inboxRead(MsgId upTo) { } if (!upTo) upTo = msgIdForRead(); - if (inboxReadTill < upTo) inboxReadTill = upTo; + inboxReadBefore = qMax(inboxReadBefore, upTo + 1); if (!dialogs.isEmpty()) { if (App::main()) App::main()->dlgUpdated(dialogs[0]); @@ -1339,7 +1339,7 @@ MsgId History::inboxRead(HistoryItem *wasRead) { MsgId History::outboxRead(int32 upTo) { if (!upTo) upTo = msgIdForRead(); - if (outboxReadTill < upTo) outboxReadTill = upTo; + if (outboxReadBefore < upTo + 1) outboxReadBefore = upTo + 1; return upTo; } @@ -1350,10 +1350,12 @@ MsgId History::outboxRead(HistoryItem *wasRead) { void History::setUnreadCount(int32 newUnreadCount, bool psUpdate) { if (unreadCount != newUnreadCount) { - if (newUnreadCount == 1 && loadedAtBottom()) { - showFrom = isEmpty() ? 0 : back()->back(); + if (newUnreadCount == 1) { + if (loadedAtBottom()) showFrom = isEmpty() ? 0 : back()->back(); + inboxReadBefore = qMax(inboxReadBefore, msgIdForRead()); } else if (!newUnreadCount) { showFrom = 0; + inboxReadBefore = qMax(inboxReadBefore, msgIdForRead() + 1); } App::histories().unreadFull += newUnreadCount - unreadCount; if (mute) App::histories().unreadMuted += newUnreadCount - unreadCount; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index c2fe29ed52..244a41cae8 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -222,7 +222,7 @@ struct History : public QList { int32 geomResize(int32 newWidth, int32 *ytransform = 0, bool dontRecountText = false); // return new size int32 width, height, msgCount, unreadCount; - int32 inboxReadTill, outboxReadTill; + int32 inboxReadBefore, outboxReadBefore; HistoryItem *showFrom; HistoryUnreadBar *unreadBar; @@ -705,14 +705,14 @@ public: return _flags & MTPDmessage_flag_out; } bool unread() const { - if ((out() && (id > 0 && id <= _history->outboxReadTill)) || (!out() && id > 0 && id <= _history->inboxReadTill)) return false; + if ((out() && (id > 0 && id < _history->outboxReadBefore)) || (!out() && id > 0 && id < _history->inboxReadBefore)) return false; return _flags & MTPDmessage_flag_unread; } bool notifyByFrom() const { return _flags & MTPDmessage_flag_notify_by_from; } bool isMediaUnread() const { - return _flags & MTPDmessage_flag_media_unread; + return (_flags & MTPDmessage_flag_media_unread) && (channelId() == NoChannel); // CHANNELS_UI } void markMediaRead() { _flags &= ~MTPDmessage_flag_media_unread; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 7786720b5d..b881a6ca98 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -789,7 +789,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); } - if (item && item->id > 0 && isUponSelected != 2 && isUponSelected != -2) { + if (item && item->id > 0 && isUponSelected != 2 && isUponSelected != -2 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage())); } if (lnkPhoto) { @@ -833,11 +833,11 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (isUponSelected > 0) { if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); - if (item && item->id > 0 && isUponSelected != 2) { + if (item && item->id > 0 && isUponSelected != 2 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage())); } } else { - if (item && item->id > 0 && isUponSelected != -2) { + if (item && item->id > 0 && isUponSelected != -2 && (!hist->peer->isChannel() || hist->peer->asChannel()->adminned)) { if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage())); } @@ -1592,6 +1592,17 @@ void MessageField::setMaxHeight(int32 maxHeight) { } } +bool MessageField::hasSendText() const { + const QString &text(getLastText()); + for (const QChar *ch = text.constData(), *e = ch + text.size(); ch != e; ++ch) { + ushort code = ch->unicode(); + if (code != ' ' && code != '\n' && code != '\r' && !replaceCharBySpace(code)) { + return true; + } + } + return false; +} + void MessageField::onChange() { int newh = ceil(document()->size().height()) + 2 * fakeMargin(), minh = st::btnSend.height - 2 * st::sendPadding; if (newh > _maxHeight) { @@ -2168,8 +2179,9 @@ bool HistoryHider::offerPeer(PeerId peer) { } else { PeerId to = offered->id; offered = 0; - parent()->onForward(to, _forwardSelected ? ForwardSelectedMessages : ForwardContextMessage); - startHide(); + if (parent()->onForward(to, _forwardSelected ? ForwardSelectedMessages : ForwardContextMessage)) { + startHide(); + } return false; } @@ -2381,7 +2393,7 @@ void HistoryWidget::onTextChange() { updateSendAction(_history, SendActionTyping); if (cHasAudioCapture()) { - if (_field.getLastText().isEmpty() && !App::main()->hasForwardingItems()) { + if (!_field.hasSendText() && !readyToForward()) { _previewCancelled = false; _send.hide(); setMouseTracking(true); @@ -2702,7 +2714,7 @@ void HistoryWidget::setKbWasHidden() { _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; _kbReplyTo = 0; - if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { _replyForwardPreviewCancel.hide(); } resizeEvent(0); @@ -2804,6 +2816,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { _peer = peerId ? App::peer(peerId) : 0; _channel = _peer ? peerToChannel(_peer->id) : NoChannel; + _canSendMessages = canSendMessages(_peer); _unblockRequest = 0; @@ -2862,7 +2875,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { setFieldText(_history->draft); _field.setFocus(); _history->draftCursor.applyTo(_field, &_synthedTextUpdate); - _replyToId = App::main()->hasForwardingItems() ? 0 : _history->draftToId; + _replyToId = readyToForward() ? 0 : _history->draftToId; if (_history->draftPreviewCancelled) { _previewCancelled = true; } @@ -2874,7 +2887,7 @@ void HistoryWidget::showPeerHistory(const PeerId &peerId, MsgId showAtMsgId) { MessageCursor cur = Local::readDraftPositions(_peer->id); cur.applyTo(_field, &_synthedTextUpdate); } - _replyToId = App::main()->hasForwardingItems() ? 0 : draft.replyTo; + _replyToId = readyToForward() ? 0 : draft.replyTo; if (draft.previewCancelled) { _previewCancelled = true; } @@ -2943,15 +2956,7 @@ void HistoryWidget::updateControlsVisibility() { } else { _scroll.show(); } - bool avail = false; - if (_peer->isUser()) { - avail = _peer->asUser()->access != UserNoAccess; - } else if (_peer->isChat()) { - avail = !_peer->asChat()->forbidden && !_peer->asChat()->left; - } else if (_peer->isChannel()) { - avail = !_peer->asChannel()->forbidden && !_peer->asChannel()->left && _peer->asChannel()->adminned; - } - if (avail) { + if (_canSendMessages) { checkMentionDropdown(); if (isBlocked()) { _botStart.hide(); @@ -2990,7 +2995,7 @@ void HistoryWidget::updateControlsVisibility() { } else { _unblock.hide(); _botStart.hide(); - if (cHasAudioCapture() && _field.getLastText().isEmpty() && !App::main()->hasForwardingItems()) { + if (cHasAudioCapture() && !_field.hasSendText() && !readyToForward()) { _send.hide(); setMouseTracking(true); mouseMoveEvent(0); @@ -3052,7 +3057,7 @@ void HistoryWidget::updateControlsVisibility() { _attachPhoto.hide(); } } - if (_replyToId || App::main()->hasForwardingItems() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { + if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { if (_replyForwardPreviewCancel.isHidden()) { _replyForwardPreviewCancel.show(); resizeEvent(0); @@ -3069,6 +3074,10 @@ void HistoryWidget::updateControlsVisibility() { _botStart.hide(); _attachDocument.hide(); _attachPhoto.hide(); + _kbScroll.hide(); + _replyForwardPreviewCancel.hide(); + _attachDocument.hide(); + _attachPhoto.hide(); _attachEmoji.hide(); _kbShow.hide(); _kbHide.hide(); @@ -3252,8 +3261,9 @@ void HistoryWidget::firstLoadMessages() { int32 from = 0, offset = 0, loadCount = MessagesPerPage; if (_showAtMsgId == ShowAtUnreadMsgId) { if (_history->unreadCount > loadCount) { - _history->getReadyFor(_showAtMsgId); - offset = _history->unreadCount - loadCount / 2; + _history->getReadyFor(_showAtMsgId) + offset = -loadCount / 2; + from = _history->inboxReadBefore; } else { _history->getReadyFor(ShowAtTheEndMsgId); } @@ -3391,7 +3401,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (!_attachType.isHidden()) _attachType.hideStart(); if (!_emojiPan.isHidden()) _emojiPan.hideStart(); - } else if (App::main()->hasForwardingItems()) { + } else if (readyToForward()) { App::main()->readServerHistory(_history, false); fastShowAtEnd(_history); App::main()->finishForwarding(_history); @@ -3897,6 +3907,23 @@ void HistoryWidget::updateDragAreas() { resizeEvent(0); } +bool HistoryWidget::canSendMessages(PeerData *peer) { + if (peer) { + if (peer->isUser()) { + return peer->asUser()->access != UserNoAccess; + } else if (peer->isChat()) { + return !peer->asChat()->forbidden && !peer->asChat()->left; + } else if (peer->isChannel()) { + return !peer->asChannel()->forbidden && !peer->asChannel()->left && peer->asChannel()->adminned; + } + } + return false; +} + +bool HistoryWidget::readyToForward() { + return _canSendMessages && !_channel && App::main()->hasForwardingItems(); +} + bool HistoryWidget::isBotStart() const { if (!_peer || !_peer->isUser() || !_peer->asUser()->botInfo) return false; return !_peer->asUser()->botInfo->startToken.isEmpty() || (_history->isEmpty() && !_history->lastMsg); @@ -3910,7 +3937,7 @@ bool HistoryWidget::updateCmdStartShown() { bool cmdStartShown = false; if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isChannel() && _peer->asChannel()->botStatus > 0) || (_peer->isUser() && _peer->asUser()->botInfo))) { if (!isBotStart() && !isBlocked() && !_keyboard.hasMarkup() && !_keyboard.forceReply()) { - if (_field.getLastText().isEmpty()) { + if (!_field.hasSendText()) { cmdStartShown = true; } } @@ -3980,7 +4007,7 @@ void HistoryWidget::onKbToggle(bool manual) { _field.setMaxHeight(st::maxFieldHeight); _kbReplyTo = 0; - if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { _replyForwardPreviewCancel.hide(); } } else { @@ -4689,18 +4716,10 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, } else if (isBotStart()) { newScrollHeight -= _botStart.height(); } else { - bool avail = false; - if (_peer->isUser()) { - avail = (_peer->asUser()->access != UserNoAccess); - } else if (_peer->isChat()) { - avail = (!_peer->asChat()->forbidden && !_peer->asChat()->left); - } else if (_peer->isChannel()) { - avail = (!_peer->asChannel()->forbidden && !_peer->asChannel()->left && _peer->asChannel()->adminned); - } - if (avail) { + if (_canSendMessages) { newScrollHeight -= (_field.height() + 2 * st::sendPadding); } - if (replyToId() || App::main()->hasForwardingItems() || (_previewData && _previewData->pendingTill >= 0)) { + if (replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { newScrollHeight -= st::replyHeight; } if (_kbShown) { @@ -4833,7 +4852,7 @@ void HistoryWidget::updateBotKeyboard() { bool hasMarkup = _keyboard.hasMarkup(), forceReply = _keyboard.forceReply() && !_replyTo; if (hasMarkup || forceReply) { if (_keyboard.singleUse() && _keyboard.hasMarkup() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) _kbWasHidden = true; - if (!isBotStart() && !isBlocked() && (wasVisible || _replyTo || (_field.getLastText().isEmpty() && !_kbWasHidden))) { + if (!isBotStart() && !isBlocked() && (wasVisible || _replyTo || (!_field.hasSendText() && !_kbWasHidden))) { if (!_showAnim.animating()) { if (hasMarkup) { _kbScroll.show(); @@ -4867,7 +4886,7 @@ void HistoryWidget::updateBotKeyboard() { _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; _kbReplyTo = 0; - if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { _replyForwardPreviewCancel.hide(); } } @@ -4882,7 +4901,7 @@ void HistoryWidget::updateBotKeyboard() { _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; _kbReplyTo = 0; - if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { _replyForwardPreviewCancel.hide(); } } @@ -5046,7 +5065,7 @@ void HistoryWidget::setFieldText(const QString &text) { void HistoryWidget::onReplyToMessage() { HistoryItem *to = App::contextItem(); - if (!to || to->id <= 0) return; + if (!to || to->id <= 0 || (_peer->isChannel() && !_peer->asChannel()->adminned)) return; App::main()->cancelForwarding(); @@ -5078,7 +5097,7 @@ void HistoryWidget::cancelReply(bool lastKeyboardUsed) { _replyTo = 0; _replyToId = 0; mouseMoveEvent(0); - if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) { + if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) { _replyForwardPreviewCancel.hide(); } @@ -5113,7 +5132,7 @@ void HistoryWidget::onReplyForwardPreviewCancel() { _saveDraftText = true; _saveDraftStart = getms(); onDraftSave(); - } else if (App::main()->hasForwardingItems()) { + } else if (readyToForward()) { App::main()->cancelForwarding(); } else if (_replyToId) { cancelReply(); @@ -5138,7 +5157,7 @@ void HistoryWidget::previewCancel() { _previewData = 0; _previewLinks.clear(); updatePreview(); - if (!_replyToId && !App::main()->hasForwardingItems() && !_kbReplyTo) _replyForwardPreviewCancel.hide(); + if (!_replyToId && !readyToForward() && !_kbReplyTo) _replyForwardPreviewCancel.hide(); } void HistoryWidget::onPreviewParse() { @@ -5239,7 +5258,7 @@ void HistoryWidget::updatePreview() { _previewTitle.setText(st::msgServiceNameFont, title, _textNameOptions); _previewDescription.setText(st::msgFont, desc, _textDlgOptions); } - } else if (!App::main()->hasForwardingItems() && !replyToId()) { + } else if (!readyToForward() && !replyToId()) { _replyForwardPreviewCancel.hide(); } resizeEvent(0); @@ -5254,6 +5273,14 @@ void HistoryWidget::onCancel() { void HistoryWidget::onFullPeerUpdated(PeerData *data) { int32 newScrollTop = _scroll.scrollTop(); if (_list && data == _peer) { + bool newCanSendMessages = canSendMessages(_peer); + if (newCanSendMessages != _canSendMessages) { + _canSendMessages = newCanSendMessages; + if (!_canSendMessages) { + cancelReply(); + } + updateControlsVisibility(); + } checkMentionDropdown(); int32 lh = _list->height(), st = _scroll.scrollTop(); _list->updateBotInfo(); @@ -5322,7 +5349,7 @@ void HistoryWidget::onDeleteSelectedSure() { } if (!ids.isEmpty()) { - App::main()->deleteMessages(ids); + App::main()->deleteMessages(_peer, ids); } onClearSelected(); @@ -5342,7 +5369,7 @@ void HistoryWidget::onDeleteContextSure() { } if (item->id > 0) { - App::main()->deleteMessages(QVector(1, MTP_int(item->id))); + App::main()->deleteMessages(item->history()->peer, QVector(1, MTP_int(item->id))); } item->destroy(); if (App::main() && App::main()->peer() == peer()) { @@ -5429,7 +5456,7 @@ void HistoryWidget::updateReplyTo(bool force) { } void HistoryWidget::updateForwarding(bool force) { - if (App::main()->hasForwardingItems()) { + if (readyToForward()) { updateControlsVisibility(); } else { resizeEvent(0); @@ -5451,7 +5478,7 @@ void HistoryWidget::updateField() { void HistoryWidget::drawField(Painter &p) { int32 backy = _field.y() - st::sendPadding, backh = _field.height() + 2 * st::sendPadding; Text *from = 0, *text = 0; - bool serviceColor = false, hasForward = App::main()->hasForwardingItems(); + bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; HistoryItem *drawReplyTo = _replyToId ? _replyTo : _kbReplyTo; if (_replyToId || (!hasForward && _kbReplyTo)) { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index a9b16823d0..61759f3af8 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -192,6 +192,18 @@ public: void focusInEvent(QFocusEvent *e); void setMaxHeight(int32 maxHeight); + bool hasSendText() const; + + static bool replaceCharBySpace(ushort code) { + // \xe2\x80[\xa8 - \xac\xad] // 8232 - 8237 + // QString from1 = QString::fromUtf8("\xe2\x80\xa8"), to1 = QString::fromUtf8("\xe2\x80\xad"); + // \xcc[\xb3\xbf\x8a] // 819, 831, 778 + // QString bad1 = QString::fromUtf8("\xcc\xb3"), bad2 = QString::fromUtf8("\xcc\xbf"), bad3 = QString::fromUtf8("\xcc\x8a"); + // [\x00\x01\x02\x07\x08\x0b-\x1f] // '\t' = 0x09 + return (code >= 0x00 && code <= 0x02) || (code >= 0x07 && code <= 0x09) || (code >= 0x0b && code <= 0x1f) || + (code == 819) || (code == 831) || (code == 778) || (code >= 8232 && code <= 8237); + } + public slots: void onChange(); @@ -624,8 +636,12 @@ private: void updateDragAreas(); + bool canSendMessages(PeerData *peer); + bool readyToForward(); + PeerData *_peer; ChannelId _channel; + bool _canSendMessages; MsgId _showAtMsgId; mtpRequestId _firstLoadRequest, _preloadRequest, _preloadDownRequest; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index b3f2714857..ea93fb38a8 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -431,7 +431,12 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr _api->init(); } -void MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { +bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { + PeerData *p = App::peer(peer); + if (!peer || p->isChannel() || (p->isChat() && p->asChat()->forbidden) || (p->isUser() && p->asUser()->access == UserNoAccess)) { + App::wnd()->showLayer(new ConfirmBox(lang(lng_forward_cant), true)); + return false; + } history.cancelReply(); _toForward.clear(); if (what == ForwardSelectedMessages) { @@ -457,6 +462,7 @@ void MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { showPeerHistory(peer, ShowAtUnreadMsgId); history.onClearSelected(); history.updateForwarding(); + return true; } bool MainWidget::hasForwardingItems() { @@ -780,8 +786,12 @@ void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHis MTP::send(MTPmessages_DeleteHistory(peer->input, d.voffset), rpcDone(&MainWidget::deleteHistoryPart, peer)); } -void MainWidget::deleteMessages(const QVector &ids) { - MTP::send(MTPmessages_DeleteMessages(MTP_vector(ids)), rpcDone(&MainWidget::messagesAffected)); +void MainWidget::deleteMessages(PeerData *peer, const QVector &ids) { + if (peer->isChannel()) { + MTP::send(MTPmessages_DeleteChannelMessages(peer->asChannel()->input, MTP_vector(ids)), rpcDone(&MainWidget::messagesAffected, peer)); + } else { + MTP::send(MTPmessages_DeleteMessages(MTP_vector(ids)), rpcDone(&MainWidget::messagesAffected, peer)); + } } void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) { @@ -1012,16 +1022,6 @@ DialogsIndexed &MainWidget::dialogsList() { return dialogs.dialogsList(); } -inline bool replaceCharBySpace(ushort code) { - // \xe2\x80[\xa8 - \xac\xad] // 8232 - 8237 - // QString from1 = QString::fromUtf8("\xe2\x80\xa8"), to1 = QString::fromUtf8("\xe2\x80\xad"); - // \xcc[\xb3\xbf\x8a] // 819, 831, 778 - // QString bad1 = QString::fromUtf8("\xcc\xb3"), bad2 = QString::fromUtf8("\xcc\xbf"), bad3 = QString::fromUtf8("\xcc\x8a"); - // [\x00\x01\x02\x07\x08\x0b-\x1f] // '\t' = 0x09 - return (code >= 0x00 && code <= 0x02) || (code >= 0x07 && code <= 0x09) || (code >= 0x0b && code <= 0x1f) || - (code == 819) || (code == 831) || (code == 778) || (code >= 8232 && code <= 8237); -} - QString cleanMessage(const QString &text) { QString result = text; QChar *_start = result.data(), *_end = _start + result.size(), *start = _start, *end = _end, *ch = start, *copy = 0; @@ -1029,7 +1029,7 @@ QString cleanMessage(const QString &text) { if (ch->unicode() == '\r') { copy = ch + 1; break; - } else if (replaceCharBySpace(ch->unicode())) { + } else if (MessageField::replaceCharBySpace(ch->unicode())) { *ch = ' '; } } @@ -1037,7 +1037,7 @@ QString cleanMessage(const QString &text) { for (; copy != end; ++copy) { if (copy->unicode() == '\r') { continue; - } else if (replaceCharBySpace(copy->unicode())) { + } else if (MessageField::replaceCharBySpace(copy->unicode())) { *ch++ = ' '; } else { *ch++ = *copy; @@ -1512,9 +1512,13 @@ void MainWidget::readRequestDone(PeerData *peer) { } } -void MainWidget::messagesAffected(const MTPmessages_AffectedMessages &result) { +void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result) { const MTPDmessages_affectedMessages &d(result.c_messages_affectedMessages()); - updPtsUpdated(d.vpts.v, d.vpts_count.v); + if (peer && peer->isChannel()) { + // CHANNELS_TODO + } else { + updPtsUpdated(d.vpts.v, d.vpts_count.v); + } } void MainWidget::videoLoadProgress(mtpFileLoader *loader) { @@ -1840,7 +1844,7 @@ void MainWidget::mediaMarkRead(const HistoryItemsMap &items) { } } if (!markedIds.isEmpty()) { - MTP::send(MTPmessages_ReadMessageContents(MTP_vector(markedIds)), rpcDone(&MainWidget::messagesAffected)); + MTP::send(MTPmessages_ReadMessageContents(MTP_vector(markedIds)), rpcDone(&MainWidget::messagesAffected, (PeerData*)0)); } } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 29cec8a01d..96fb89df96 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -272,7 +272,7 @@ public: void shareContactLayer(UserData *contact); void hiderLayer(HistoryHider *h); void noHider(HistoryHider *destroyed); - void onForward(const PeerId &peer, ForwardWhatMessages what); + bool onForward(const PeerId &peer, ForwardWhatMessages what); void onShareContact(const PeerId &peer, UserData *contact); void onSendPaths(const PeerId &peer); void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data); @@ -285,7 +285,7 @@ public: bool leaveChatFailed(PeerData *peer, const RPCError &e); void deleteHistoryAfterLeave(PeerData *peer, const MTPUpdates &updates); void deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result); - void deleteMessages(const QVector &ids); + void deleteMessages(PeerData *peer, const QVector &ids); void deletedContact(UserData *user, const MTPcontacts_Link &result); void deleteConversation(PeerData *peer); void clearHistory(PeerData *peer); @@ -453,7 +453,7 @@ private: bool readRequestFail(PeerData *peer, const RPCError &error); void readRequestDone(PeerData *peer); - void messagesAffected(const MTPmessages_AffectedMessages &result); + void messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result); void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); bool _started; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 0fc023c949..300942606a 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -64,7 +64,7 @@ _docRadialFirst(0), _docRadialStart(0), _docRadialLast(0), _docRadialOpacity(1), _docDownload(this, lang(lng_media_download), st::mvDocLink), _docSaveAs(this, lang(lng_mediaview_save_as), st::mvDocLink), _docCancel(this, lang(lng_cancel), st::mvDocLink), -_history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _channel(NoChannel), +_history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _channel(NoChannel), _canForward(false), _canDelete(false), _loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction), _ignoringDropdown(false), _controlsState(ControlsShown), _controlsAnimStarted(0), _menu(0), _dropdown(this, st::mvDropdown), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRightButton(false), @@ -314,8 +314,8 @@ void MediaView::updateDropdown() { _btnShowInFolder->setVisible(_doc && !_doc->already(true).isEmpty()); _btnSaveAs->setVisible(true); _btnCopy->setVisible((_doc && !_current.isNull()) || (_photo && _photo->full->loaded())); - _btnForward->setVisible(_msgid > 0 && !_channel); - _btnDelete->setVisible(_msgid > 0 || (_photo && App::self() && App::self()->photoId == _photo->id) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id)); + _btnForward->setVisible(_canForward); + _btnDelete->setVisible(_canDelete || (_photo && App::self() && App::self()->photoId == _photo->id) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id)); _btnViewAll->setVisible((_overview != OverviewCount) && _history); _btnViewAll->setText(lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all)); _dropdown.updateButtons(); @@ -655,6 +655,8 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { _index = -1; _msgid = context ? context->id : 0; _channel = context ? context->channelId() : NoChannel; + _canForward = _msgid > 0 && (_channel == NoChannel); + _canDelete = (_channel == NoChannel) || context->history()->peer->asChannel()->adminned; _photo = photo; if (_history) { _overview = OverviewPhotos; @@ -682,6 +684,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { _msgid = 0; _channel = NoChannel; + _canForward = _canDelete = false; _index = -1; _photo = photo; _overview = OverviewCount; @@ -725,6 +728,8 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { _index = -1; _msgid = context ? context->id : 0; _channel = context ? context->channelId() : NoChannel; + _canForward = _msgid > 0 && (_channel == NoChannel); + _canDelete = (_channel == NoChannel) || context->history()->peer->asChannel()->adminned; if (_history) { _overview = OverviewDocuments; @@ -1385,6 +1390,8 @@ void MediaView::moveToNext(int32 delta) { if (HistoryItem *item = App::histItemById(_history->channelId(), _history->_overview[_overview][_index])) { _msgid = item->id; _channel = item->channelId(); + _canForward = _msgid > 0 && (_channel == NoChannel); + _canDelete = (_channel == NoChannel) || item->history()->peer->asChannel()->adminned; if (item->getMedia()) { switch (item->getMedia()->type()) { case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index b4e4835e07..f6bb18db88 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -161,6 +161,7 @@ private: int32 _index; // index in photos or files array, -1 if just photo MsgId _msgid; // msgId of current photo or file ChannelId _channel; + bool _canForward, _canDelete; mtpRequestId _loadRequest; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index a2c09242ec..4606082b98 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -856,6 +856,10 @@ void OverviewInner::touchScrollUpdated(const QPoint &screenPos) { void OverviewInner::applyDragSelection() { if (_dragSelFromIndex < 0 || _dragSelToIndex < 0) return; + if (_peer && _peer->isChannel() && !_peer->asChannel()->adminned) { + _selected.clear(); + return; + } if (!_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) { _selected.clear(); } @@ -1781,12 +1785,16 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_clear_selection), _overview, SLOT(onClearSelected())); } else if (App::hoveredLinkItem()) { if (isUponSelected != -2) { - if (dynamic_cast(App::hoveredLinkItem()) && !_hist->peer->isChannel()) { + if (App::hoveredLinkItem()->toHistoryMessage() && !_peer->isChannel()) { _menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true); } - _menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true); + if (!_peer->isChannel() || _peer->asChannel()->adminned) { + _menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true); + } + } + if (App::hoveredLinkItem()->id > 0 && (!_peer->isChannel() || _peer->asChannel()->adminned)) { + _menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true); } - _menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true); } App::contextItem(App::hoveredLinkItem()); updateMsg(App::contextItem()); @@ -1815,12 +1823,16 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_clear_selection), _overview, SLOT(onClearSelected())); } else { if (isUponSelected != -2) { - if (dynamic_cast(App::mousedItem()) && !_hist->peer->isChannel()) { + if (App::mousedItem()->toHistoryMessage() && !_peer->isChannel()) { _menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true); } - _menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true); + if (!_peer->isChannel() || _peer->asChannel()->adminned) { + _menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true); + } + } + if (App::mousedItem()->id > 0 && (!_peer->isChannel() || _peer->asChannel()->adminned)) { + _menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true); } - _menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true); } App::contextItem(App::mousedItem()); updateMsg(App::contextItem()); @@ -1931,7 +1943,7 @@ void OverviewInner::deleteMessage() { HistoryItem *item = App::contextItem(); if (!item || item->itemType() != HistoryItem::MsgType) return; - HistoryMessage *msg = dynamic_cast(item); + HistoryMessage *msg = item->toHistoryMessage(); App::main()->deleteLayer((msg && msg->uploading()) ? -2 : -1); } @@ -2098,7 +2110,7 @@ void OverviewInner::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { HistoryItem *item = App::histItemById(_channel, i.key()); - if (dynamic_cast(item) && item->id > 0) { + if (item && item->toHistoryMessage() && item->id > 0) { sel.insert(item->id, item); } } @@ -2924,7 +2936,7 @@ void OverviewWidget::onDeleteSelectedSure() { } if (!ids.isEmpty()) { - App::main()->deleteMessages(ids); + App::main()->deleteMessages(peer(), ids); } onClearSelected(); @@ -2944,7 +2956,7 @@ void OverviewWidget::onDeleteContextSure() { } if (item->id > 0) { - App::main()->deleteMessages(QVector(1, MTP_int(item->id))); + App::main()->deleteMessages(item->history()->peer, QVector(1, MTP_int(item->id))); } item->destroy(); if (App::main() && App::main()->peer() == peer()) { diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index ef52464536..a4039296d4 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -245,7 +245,7 @@ namespace { Gdiplus::Status gdiRes = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); if (gdiRes != Gdiplus::Ok) { - DEBUG_LOG(("Application Error: could not init GDI+, error: %1").arg((int)gdiRes)); + LOG(("Application Error: could not init GDI+, error: %1").arg((int)gdiRes)); return false; } blend.AlphaFormat = AC_SRC_ALPHA; @@ -254,7 +254,10 @@ namespace { blend.BlendOp = AC_SRC_OVER; screenDC = GetDC(0); - if (!screenDC) return false; + if (!screenDC) { + LOG(("Application Error: could not GetDC(0), error: %2").arg(GetLastError())); + return false; + } QRect avail(App::app() ? App::app()->desktop()->availableGeometry() : QDesktopWidget().availableGeometry()); max_w = avail.width(); @@ -283,14 +286,14 @@ namespace { wc.lpszClassName = _cn; wc.hIconSm = 0; if (!RegisterClassEx(&wc)) { - DEBUG_LOG(("Application Error: could not register shadow window class %1, error: %2").arg(i).arg(GetLastError())); + LOG(("Application Error: could not register shadow window class %1, error: %2").arg(i).arg(GetLastError())); destroy(); return false; } hwnds[i] = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0); if (!hwnds[i]) { - DEBUG_LOG(("Application Error: could not create shadow window class %1, error: %2").arg(i).arg(GetLastError())); + LOG(("Application Error: could not create shadow window class %1, error: %2").arg(i).arg(GetLastError())); destroy(); return false; } @@ -298,14 +301,14 @@ namespace { dcs[i] = CreateCompatibleDC(screenDC); if (!dcs[i]) { - DEBUG_LOG(("Application Error: could not create dc for shadow window class %1, error: %2").arg(i).arg(GetLastError())); + LOG(("Application Error: could not create dc for shadow window class %1, error: %2").arg(i).arg(GetLastError())); destroy(); return false; } bitmaps[i] = CreateCompatibleBitmap(screenDC, (i % 2) ? _size : max_w, (i % 2) ? max_h : _size); if (!bitmaps[i]) { - DEBUG_LOG(("Application Error: could not create bitmap for shadow window class %1, error: %2").arg(i).arg(GetLastError())); + LOG(("Application Error: could not create bitmap for shadow window class %1, error: %2").arg(i).arg(GetLastError())); destroy(); return false; }