diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 60cae09bbd..de94b6a60c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -671,13 +671,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_action_game_you_scored" = "You scored {count:#|#|#} in {game}"; "lng_action_game_score_no_game" = "{from} scored {count:#|#|#}"; "lng_action_game_you_scored_no_game" = "You scored {count:#|#|#}"; -"lng_action_call_outgoing" = "Outgoing call at {time}"; -"lng_action_call_incoming" = "Incoming call at {time}"; -"lng_action_call_outgoing_duration" = "Outgoing call ({duration}) at {time}"; -"lng_action_call_incoming_duration" = "Incoming call ({duration}) at {time}"; -"lng_action_call_incoming_missed" = "Missed call at {time}"; -"lng_action_call_outgoing_missed" = "Cancelled call at {time}"; -"lng_action_call_incoming_declined" = "Declined call at {time}"; "lng_action_payment_done" = "You have just successfully transferred {amount} to {user}"; "lng_action_payment_done_for" = "You have just successfully transferred {amount} to {user} for {invoice}"; @@ -1145,6 +1138,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_call_box_status_date" = "{date} at {time}"; "lng_call_box_status_group" = "({count}) {status}"; +"lng_call_outgoing" = "Outgoing call"; +"lng_call_incoming" = "Incoming call"; +"lng_call_missed" = "Missed call"; +"lng_call_cancelled" = "Cancelled call"; +"lng_call_declined" = "Declined call"; +"lng_call_duration_info" = "{time}, {duration}"; +"lng_call_type_and_duration" = "{type} ({duration})"; + // Not used "lng_topbar_info" = "Info"; diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index b08be61fbc..030b0e356e 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "observer_peer.h" #include "ui/effects/ripple_animation.h" #include "calls/calls_instance.h" +#include "history/history_media_types.h" namespace Calls { namespace { @@ -86,7 +87,7 @@ private: return false; } QSize actionSize() const override { - return QSize(st::callReDial.width, st::callReDial.height); + return peer()->isUser() ? QSize(st::callReDial.width, st::callReDial.height) : QSize(); } QMargins actionMargins() const override { return QMargins(0, 0, 0, 0); @@ -157,10 +158,12 @@ void BoxController::Row::refreshStatus() { BoxController::Row::Type BoxController::Row::ComputeType(HistoryItem *item) { if (item->out()) { return Type::Out; - } else if (auto call = item->Get()) { - using Reason = HistoryMessageCallInfo::Reason; - if (call->reason == Reason::Busy || call->reason == Reason::Missed) { - return Type::Missed; + } else if (auto media = item->getMedia()) { + if (media->type() == MediaTypeCall) { + auto reason = static_cast(media)->reason(); + if (reason == HistoryCall::FinishReason::Busy || reason == HistoryCall::FinishReason::Missed) { + return Type::Missed; + } } } return Type::In; @@ -243,7 +246,7 @@ void BoxController::rowClicked(PeerListBox::Row *row) { void BoxController::rowActionClicked(PeerListBox::Row *row) { auto user = row->peer()->asUser(); - Expects(user != nullptr); + t_assert(user != nullptr); Current().startOutgoingCall(user); } @@ -258,7 +261,7 @@ void BoxController::receivedCalls(const QVector &result) { auto peerId = peerFromMessage(message); if (auto peer = App::peerLoaded(peerId)) { auto item = App::histories().addNewMessage(message, NewMessageExisting); - appendRow(item); + insertRow(item, InsertWay::Append); } else { LOG(("API Error: a search results with not loaded peer %1").arg(peerId)); } @@ -269,12 +272,14 @@ void BoxController::receivedCalls(const QVector &result) { view()->refreshRows(); } -bool BoxController::appendRow(HistoryItem *item) { +bool BoxController::insertRow(HistoryItem *item, InsertWay way) { if (auto row = rowForItem(item)) { - row->addItem(item); - return false; + if (row->canAddItem(item)) { + row->addItem(item); + return false; + } } - view()->appendRow(createRow(item)); + (way == InsertWay::Append) ? view()->appendRow(createRow(item)) : view()->prependRow(createRow(item)); view()->reorderRows([](auto &begin, auto &end) { std::sort(begin, end, [](auto &a, auto &b) { return static_cast(*a).maxItemId() > static_cast(*a).maxItemId(); @@ -283,29 +288,17 @@ bool BoxController::appendRow(HistoryItem *item) { return true; } -bool BoxController::prependRow(HistoryItem *item) { - if (auto row = rowForItem(item)) { - row->addItem(item); - return false; - } - view()->prependRow(createRow(item)); - return true; -} - BoxController::Row *BoxController::rowForItem(HistoryItem *item) { auto v = view(); - auto checkForReturn = [item](Row *row) { - return row->canAddItem(item) ? row : nullptr; - }; if (auto fullRowsCount = v->fullRowsCount()) { auto itemId = item->id; auto lastRow = static_cast(v->rowAt(fullRowsCount - 1)); if (itemId < lastRow->minItemId()) { - return checkForReturn(lastRow); + return lastRow; } auto firstRow = static_cast(v->rowAt(0)); if (itemId > firstRow->maxItemId()) { - return checkForReturn(firstRow); + return firstRow; } // Binary search. Invariant: @@ -332,7 +325,7 @@ BoxController::Row *BoxController::rowForItem(HistoryItem *item) { return possibleResult; } } - return checkForReturn(result); + return result; } return nullptr; } diff --git a/Telegram/SourceFiles/calls/calls_box_controller.h b/Telegram/SourceFiles/calls/calls_box_controller.h index 8314f0ff44..4746e0ebd9 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.h +++ b/Telegram/SourceFiles/calls/calls_box_controller.h @@ -38,8 +38,11 @@ private: class Row; Row *rowForItem(HistoryItem *item); - bool appendRow(HistoryItem *item); - bool prependRow(HistoryItem *item); + enum class InsertWay { + Append, + Prepend, + }; + bool insertRow(HistoryItem *item, InsertWay way); std::unique_ptr createRow(HistoryItem *item) const; MsgId _offsetId = 0; diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 77410f3fd9..bd44aaacaa 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -31,6 +31,7 @@ Instance::Instance() = default; void Instance::startOutgoingCall(gsl::not_null user) { if (_currentCall) { + _currentCallPanel->showAndActivate(); return; // Already in a call. } createCall(user, Call::Type::Outgoing); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 247f885996..43d10e09e9 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -835,7 +835,11 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, case mtpc_messageService: { auto &m = msg.c_messageService(); - result = HistoryService::create(this, m); + if (m.vaction.type() == mtpc_messageActionPhoneCall) { + result = HistoryMessage::create(this, m); + } else { + result = HistoryService::create(this, m); + } if (applyServiceAction) { auto &action = m.vaction; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 588162bbd5..e811668d10 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -110,6 +110,7 @@ enum HistoryMediaType { MediaTypePhoto, MediaTypeVideo, MediaTypeContact, + MediaTypeCall, MediaTypeFile, MediaTypeGif, MediaTypeSticker, diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 6a2c975495..a94ab26c6a 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -402,6 +402,18 @@ historyCallArrowMissedIn: icon {{ "call_arrow_in", historyCallArrowMissedInFg }} historyCallArrowMissedInSelected: icon {{ "call_arrow_in", historyCallArrowMissedInFgSelected }}; historyCallArrowOut: icon {{ "call_arrow_out", historyCallArrowOutFg }}; historyCallArrowOutSelected: icon {{ "call_arrow_out", historyCallArrowOutFgSelected }}; +historyCallWidth: 240px; +historyCallHeight: 56px; +historyCallInIcon: icon {{ "menu_calls", msgFileInBg }}; +historyCallInIconSelected: icon {{ "menu_calls", msgFileInBgSelected }}; +historyCallOutIcon: icon {{ "menu_calls", msgFileOutBg }}; +historyCallOutIconSelected: icon {{ "menu_calls", msgFileOutBgSelected }}; +historyCallIconPosition: point(17px, 18px); +historyCallLeft: 16px; +historyCallTop: 9px; +historyCallStatusTop: 29px; +historyCallStatusSkip: 4px; +historyCallArrowPosition: point(-1px, 1px); msgFileMenuSize: size(36px, 36px); msgFileSize: 44px; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 6f9d2a17fe..02260313dc 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1007,8 +1007,11 @@ void HistoryInner::onDragExec() { auto drag = std::make_unique(App::wnd()); if (!urls.isEmpty()) mimeData->setUrls(urls); - if (uponSelected && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && !Adaptive::OneColumn()) { - mimeData->setData(qsl("application/x-td-forward-selected"), "1"); + if (uponSelected && !Adaptive::OneColumn()) { + auto selectedState = getSelectionState(); + if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) { + mimeData->setData(qsl("application/x-td-forward-selected"), "1"); + } } drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); @@ -1213,9 +1216,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { dragActionUpdate(e->globalPos()); } - int32 selectedForForward, selectedForDelete; - getSelectionState(selectedForForward, selectedForDelete); - bool canSendMessages = _widget->canSendMessages(_peer); + auto selectedState = getSelectionState(); + auto canSendMessages = _widget->canSendMessages(_peer); // -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items int32 isUponSelected = 0, hasSelected = 0;; @@ -1296,8 +1298,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_copy_post_link), _widget, SLOT(onCopyPostLink())); } if (isUponSelected > 1) { - _menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected())); - if (selectedForDelete == selectedForForward) { + if (selectedState.count > 0 && selectedState.canForwardCount == selectedState.count) { + _menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected())); + } + if (selectedState.count > 0 && selectedState.canDeleteCount == selectedState.count) { _menu->addAction(lang(lng_context_delete_selected), base::lambda_guarded(this, [this] { _widget->confirmDeleteSelectedItems(); })); @@ -1305,7 +1309,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_clear_selection), _widget, SLOT(onClearSelected())); } else if (App::hoveredLinkItem()) { if (isUponSelected != -2) { - if (dynamic_cast(App::hoveredLinkItem()) && App::hoveredLinkItem()->id > 0) { + if (App::hoveredLinkItem()->canForward()) { _menu->addAction(lang(lng_context_forward_msg), _widget, SLOT(forwardMessage()))->setEnabled(true); } if (App::hoveredLinkItem()->canDelete()) { @@ -1321,9 +1325,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } else { // maybe cursor on some text history item? bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); - bool canForward = item && (item->id > 0) && !item->serviceMsg(); + bool canForward = item && item->canForward(); - HistoryMessage *msg = dynamic_cast(item); + auto msg = dynamic_cast(item); if (isUponSelected > 0) { _menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true); if (item && item->id > 0 && isUponSelected != 2) { @@ -1391,7 +1395,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } - QString linkCopyToClipboardText = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItemText() : QString(); + auto linkCopyToClipboardText = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItemText() : QString(); if (!linkCopyToClipboardText.isEmpty()) { _menu->addAction(linkCopyToClipboardText, this, SLOT(copyContextUrl()))->setEnabled(true); } @@ -1399,8 +1403,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_copy_post_link), _widget, SLOT(onCopyPostLink())); } if (isUponSelected > 1) { - _menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected())); - if (selectedForDelete == selectedForForward) { + if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) { + _menu->addAction(lang(lng_context_forward_selected), _widget, SLOT(onForwardSelected())); + } + if (selectedState.count > 0 && selectedState.count == selectedState.canDeleteCount) { _menu->addAction(lang(lng_context_delete_selected), base::lambda_guarded(this, [this] { _widget->confirmDeleteSelectedItems(); })); @@ -1595,9 +1601,8 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { setToClipboard(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { - int32 selectedForForward, selectedForDelete; - getSelectionState(selectedForForward, selectedForDelete); - if (!_selected.isEmpty() && selectedForDelete == selectedForForward) { + auto selectedState = getSelectionState(); + if (selectedState.count > 0 && selectedState.canDeleteCount == selectedState.count) { _widget->confirmDeleteSelectedItems(); } } else { @@ -1957,25 +1962,26 @@ bool HistoryInner::canCopySelected() const { } bool HistoryInner::canDeleteSelected() const { - if (_selected.isEmpty() || _selected.cbegin().value() != FullSelection) return false; - int32 selectedForForward, selectedForDelete; - getSelectionState(selectedForForward, selectedForDelete); - return (selectedForForward == selectedForDelete); + auto selectedState = getSelectionState(); + return (selectedState.count > 0) && (selectedState.count == selectedState.canDeleteCount); } -void HistoryInner::getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const { - selectedForForward = selectedForDelete = 0; +Window::TopBarWidget::SelectedState HistoryInner::getSelectionState() const { + auto result = Window::TopBarWidget::SelectedState {}; for (auto i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { if (i.value() == FullSelection) { + ++result.count; if (i.key()->canDelete()) { - ++selectedForDelete; + ++result.canDeleteCount; } - ++selectedForForward; + if (i.key()->canForward()) { + ++result.canForwardCount; + } + } else { + result.textSelected = true; } } - if (!selectedForDelete && !selectedForForward && !_selected.isEmpty()) { // text selection - selectedForForward = -1; - } + return result; } void HistoryInner::clearSelectedItems(bool onlyTextSelection) { @@ -1989,8 +1995,8 @@ void HistoryInner::clearSelectedItems(bool onlyTextSelection) { void HistoryInner::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { if (_selected.isEmpty() || _selected.cbegin().value() != FullSelection) return; - for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { - HistoryItem *item = i.key(); + for (auto i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { + auto item = i.key(); if (item && item->toHistoryMessage() && item->id > 0) { if (item->history() == _migrated) { sel.insert(item->id - ServerMaxMsgId, item); @@ -2437,7 +2443,7 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const { QString HistoryInner::tooltipText() const { if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) { if (App::hoveredItem()) { - QString dateText = App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); + auto dateText = App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); if (auto edited = App::hoveredItem()->Get()) { dateText += '\n' + lng_edited_date(lt_date, edited->_editDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); } @@ -2449,7 +2455,7 @@ QString HistoryInner::tooltipText() const { return forwarded->_text.originalText(AllTextSelection, ExpandLinksNone); } } - } else if (ClickHandlerPtr lnk = ClickHandler::getActive()) { + } else if (auto lnk = ClickHandler::getActive()) { return lnk->tooltip(); } return QString(); diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 29abe7800d..c1f554054d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/tooltip.h" #include "ui/widgets/scroll_area.h" +#include "window/top_bar_widget.h" namespace Window { class Controller; @@ -59,7 +60,7 @@ public: bool canCopySelected() const; bool canDeleteSelected() const; - void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const; + Window::TopBarWidget::SelectedState getSelectionState() const; void clearSelectedItems(bool onlyTextSelection = false); void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true); void selectItem(HistoryItem *item); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 2f6d19f0ca..966f653aab 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -757,6 +757,21 @@ bool HistoryItem::canPin() const { return id > 0 && _history->peer->isMegagroup() && (_history->peer->asChannel()->amEditor() || _history->peer->asChannel()->amCreator()) && toHistoryMessage(); } +bool HistoryItem::canForward() const { + if (id < 0) { + return false; + } + if (auto message = toHistoryMessage()) { + if (auto media = message->getMedia()) { + if (media->type() == MediaTypeCall) { + return false; + } + } + return true; + } + return false; +} + bool HistoryItem::canEdit(const QDateTime &cur) const { auto messageToMyself = (_history->peer->id == AuthSession::CurrentUserPeerId()); auto messageTooOld = messageToMyself ? false : (date.secsTo(cur) >= Global::EditTimeLimit()); @@ -816,6 +831,11 @@ bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const { if (!toHistoryMessage()) { return false; } + if (auto media = getMedia()) { + if (media->type() == MediaTypeCall) { + return false; + } + } if (!out()) { if (auto chat = history()->peer->asChat()) { if (!chat->amCreator() && (!chat->amAdmin() || !chat->adminsEnabled())) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index b307baf868..743e787f06 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -413,15 +413,6 @@ struct HistoryMessageUnreadBar : public RuntimeComponent { - enum class Reason { - None, - Missed, - Busy, - }; - Reason reason = Reason::None; -}; - // HistoryMedia has a special owning smart pointer // which regs/unregs this media to the holding HistoryItem class HistoryMedia; @@ -694,6 +685,7 @@ public: } bool canPin() const; + bool canForward() const; bool canEdit(const QDateTime &cur) const; bool canDelete() const; bool canDeleteForEveryone(const QDateTime &cur) const; @@ -974,6 +966,7 @@ public: result->finishCreate(); return result; } + }; ClickHandlerPtr goToMessageClickHandler(PeerData *peer, MsgId msgId); diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 37c99000c8..e2cb8d5257 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_location_manager.h" #include "window/window_controller.h" #include "styles/style_history.h" +#include "calls/calls_instance.h" namespace { @@ -2787,6 +2788,123 @@ void HistoryContact::updateSentMedia(const MTPMessageMedia &media) { } } +HistoryCall::HistoryCall(HistoryItem *parent, const MTPDmessageActionPhoneCall &call) : HistoryMedia(parent) +, _reason(GetReason(call)) { + if (_parent->out()) { + _text = lang(_reason == FinishReason::Missed ? lng_call_cancelled : lng_call_outgoing); + } else if (_reason == FinishReason::Missed) { + _text = lang(lng_call_missed); + } else if (_reason == FinishReason::Busy) { + _text = lang(lng_call_declined); + } else { + _text = lang(lng_call_incoming); + } + _duration = call.has_duration() ? call.vduration.v : 0; + + _status = _parent->date.time().toString(cTimeFormat()); + if (_duration) { + if (_reason != FinishReason::Missed && _reason != FinishReason::Busy) { + _status = lng_call_duration_info(lt_time, _status, lt_duration, formatDurationWords(_duration)); + } else { + _duration = 0; + } + } +} + +HistoryCall::FinishReason HistoryCall::GetReason(const MTPDmessageActionPhoneCall &call) { + if (call.has_reason()) { + switch (call.vreason.type()) { + case mtpc_phoneCallDiscardReasonBusy: return FinishReason::Busy; + case mtpc_phoneCallDiscardReasonDisconnect: return FinishReason::Disconnected; + case mtpc_phoneCallDiscardReasonHangup: return FinishReason::Hangup; + case mtpc_phoneCallDiscardReasonMissed: return FinishReason::Missed; + } + Unexpected("Call reason type."); + } + return FinishReason::Hangup; +} + +void HistoryCall::initDimensions() { + _maxw = st::msgFileMinWidth; + + _link = MakeShared([peer = _parent->history()->peer] { + if (auto user = peer->asUser()) { + Calls::Current().startOutgoingCall(user); + } + }); + + _maxw = st::historyCallWidth; + _minh = st::historyCallHeight; + if (!isBubbleTop()) { + _minh -= st::msgFileTopMinus; + } + _height = _minh; +} + +void HistoryCall::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { + if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + auto skipx = 0, skipy = 0, width = _width, height = _height; + + auto out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost; + auto selected = (selection == FullSelection); + + if (width >= _maxw) { + width = _maxw; + } + + auto nameleft = 0, nametop = 0, nameright = 0, statustop = 0; + auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; + + nameleft = st::historyCallLeft; + nametop = st::historyCallTop - topMinus; + nameright = st::msgFilePadding.left(); + statustop = st::historyCallStatusTop - topMinus; + + auto namewidth = width - nameleft - nameright; + + p.setFont(st::semiboldFont); + p.setPen(outbg ? (selected ? st::historyFileNameOutFgSelected : st::historyFileNameOutFg) : (selected ? st::historyFileNameInFgSelected : st::historyFileNameInFg)); + p.drawTextLeft(nameleft, nametop, width, _text); + + auto statusleft = nameleft; + auto missed = (_reason == FinishReason::Missed || _reason == FinishReason::Busy); + auto &arrow = outbg ? (selected ? st::historyCallArrowOutSelected : st::historyCallArrowOut) : missed ? (selected ? st::historyCallArrowMissedInSelected : st::historyCallArrowMissedIn) : (selected ? st::historyCallArrowInSelected : st::historyCallArrowIn); + arrow.paint(p, statusleft + st::historyCallArrowPosition.x(), statustop + st::historyCallArrowPosition.y(), width); + statusleft += arrow.width() + st::historyCallStatusSkip; + + auto &statusFg = outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg); + p.setFont(st::normalFont); + p.setPen(statusFg); + p.drawTextLeft(statusleft, statustop, width, _status); + + auto &icon = outbg ? (selected ? st::historyCallOutIconSelected : st::historyCallOutIcon) : (selected ? st::historyCallInIconSelected : st::historyCallInIcon); + icon.paint(p, width - st::historyCallIconPosition.x() - icon.width(), st::historyCallIconPosition.y() - topMinus, width); +} + +HistoryTextState HistoryCall::getState(int x, int y, HistoryStateRequest request) const { + HistoryTextState result; + if (x >= 0 && y >= 0 && x < _width && y < _height) { + result.link = _link; + return result; + } + return result; +} + +QString HistoryCall::notificationText() const { + auto result = _text; + if (_duration > 0) { + result = lng_call_type_and_duration(lt_type, result, lt_duration, formatDurationWords(_duration)); + } + return result; +} + +TextWithEntities HistoryCall::selectedText(TextSelection selection) const { + if (selection != FullSelection) { + return TextWithEntities(); + } + return { qsl("[ ") + _text + qsl(" ]"), EntitiesInText() }; +} + namespace { QString siteNameFromUrl(const QString &url) { diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 7a31e00eb9..f0b4e55b0a 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -709,6 +709,61 @@ private: }; +class HistoryCall : public HistoryMedia { +public: + HistoryCall(HistoryItem *parent, const MTPDmessageActionPhoneCall &call); + HistoryMediaType type() const override { + return MediaTypeCall; + } + std::unique_ptr clone(HistoryItem *newParent) const override { + Unexpected("Clone HistoryCall."); + } + + void initDimensions() override; + + void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; + HistoryTextState getState(int x, int y, HistoryStateRequest request) const override; + + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return true; + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return false; + } + + QString notificationText() const override; + TextWithEntities selectedText(TextSelection selection) const override; + + bool needsBubble() const override { + return true; + } + bool customInfoLayout() const override { + return true; + } + + enum class FinishReason { + Missed, + Busy, + Disconnected, + Hangup, + }; + FinishReason reason() const { + return _reason; + } + +private: + static FinishReason GetReason(const MTPDmessageActionPhoneCall &call); + + FinishReason _reason = FinishReason::Missed; + int _duration = 0; + + QString _text; + QString _status; + + ClickHandlerPtr _link; + +}; + class HistoryWebPage : public HistoryMedia { public: HistoryWebPage(HistoryItem *parent, WebPageData *data); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 739220788d..e7c2a65e83 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -423,6 +423,25 @@ HistoryMessage::HistoryMessage(History *history, const MTPDmessage &msg) setText(textWithEntities); } +HistoryMessage::HistoryMessage(History *history, const MTPDmessageService &msg) + : HistoryItem(history, msg.vid.v, mtpCastFlags(msg.vflags.v), ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) { + CreateConfig config; + + if (msg.has_reply_to_msg_id()) config.replyTo = msg.vreply_to_msg_id.v; + + createComponents(config); + + switch (msg.vaction.type()) { + case mtpc_messageActionPhoneCall: { + _media = std::make_unique(this, msg.vaction.c_messageActionPhoneCall()); + } break; + + default: Unexpected("Service message action type in HistoryMessage."); + } + + setText(TextWithEntities {}); +} + namespace { MTPDmessage::Flags newForwardedFlags(PeerData *p, int32 from, HistoryMessage *fwd) { @@ -1972,44 +1991,6 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return result; }; - auto preparePhoneCallText = [this](const MTPDmessageActionPhoneCall &action) { - auto result = PreparedText {}; - auto timeText = date.toString(cTimeFormat()); - auto duration = action.has_duration() ? qMax(action.vduration.v, 0) : 0; - auto durationText = ([duration]() -> QString { - if (!duration) { - return QString(); - } - if (duration >= 60) { - auto minutes = duration / 60; - auto seconds = duration % 60; - return lng_duration_minutes_seconds(lt_count_minutes, minutes, lt_count_seconds, seconds); - } - return lng_duration_seconds(lt_count, duration); - })(); - auto info = this->Get(); - if (out()) { - if (info && info->reason == HistoryMessageCallInfo::Reason::Missed) { - result.text = lng_action_call_outgoing_missed(lt_time, timeText); - } else if (duration) { - result.text = lng_action_call_outgoing_duration(lt_duration, durationText, lt_time, timeText); - } else { - result.text = lng_action_call_outgoing(lt_time, timeText); - } - } else { - if (info && info->reason == HistoryMessageCallInfo::Reason::Missed) { - result.text = lng_action_call_incoming_missed(lt_time, timeText); - } else if (info && info->reason == HistoryMessageCallInfo::Reason::Busy) { - result.text = lng_action_call_incoming_declined(lt_time, timeText); - } else if (duration) { - result.text = lng_action_call_incoming_duration(lt_duration, durationText, lt_time, timeText); - } else { - result.text = lng_action_call_incoming(lt_time, timeText); - } - } - return result; - }; - auto messageText = PreparedText {}; switch (action.type()) { @@ -2026,7 +2007,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { case mtpc_messageActionChannelMigrateFrom: messageText.text = lang(lng_action_group_migrate); break; case mtpc_messageActionPinMessage: messageText = preparePinnedText(); break; case mtpc_messageActionGameScore: messageText = prepareGameScoreText(); break; - case mtpc_messageActionPhoneCall: messageText = preparePhoneCallText(action.c_messageActionPhoneCall()); break; + case mtpc_messageActionPhoneCall: Unexpected("PhoneCall type in HistoryService."); case mtpc_messageActionPaymentSent: messageText = preparePaymentSentText(); break; default: messageText.text = lang(lng_message_empty); break; } @@ -2438,22 +2419,6 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { auto amount = message.vaction.c_messageActionPaymentSent().vtotal_amount.v; auto currency = qs(message.vaction.c_messageActionPaymentSent().vcurrency); Get()->amount = HistoryInvoice::fillAmountAndCurrency(amount, currency); - } else if (message.vaction.type() == mtpc_messageActionPhoneCall) { - using Reason = HistoryMessageCallInfo::Reason; - auto &action = message.vaction.c_messageActionPhoneCall(); - auto reason = ([&action] { - if (action.has_reason()) { - switch (action.vreason.type()) { - case mtpc_phoneCallDiscardReasonBusy: return Reason::Busy; - case mtpc_phoneCallDiscardReasonMissed: return Reason::Missed; - } - } - return Reason::None; - })(); - if (reason != Reason::None) { - UpdateComponents(HistoryMessageCallInfo::Bit()); - Get()->reason = reason; - } } if (message.has_reply_to_msg_id()) { if (message.vaction.type() == mtpc_messageActionPinMessage) { diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 0fec75c0a2..7096c78672 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -27,6 +27,9 @@ public: static HistoryMessage *create(History *history, const MTPDmessage &msg) { return _create(history, msg); } + static HistoryMessage *create(History *history, const MTPDmessageService &msg) { + return _create(history, msg); + } static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd) { return _create(history, msgId, flags, date, from, fwd); } @@ -147,6 +150,7 @@ public: private: HistoryMessage(History *history, const MTPDmessage &msg); + HistoryMessage(History *history, const MTPDmessageService &msg); HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd); // local forwarded HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities); // local message HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 820bd6f209..3745f3adfb 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -982,7 +982,7 @@ void HistoryWidget::setInnerFocus() { if (_scroll->isHidden()) { setFocus(); } else if (_list) { - if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { + if (_nonEmptySelection || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { _list->setFocus(); } else { _field->setFocus(); @@ -1748,8 +1748,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _titlePeerTextWidth = 0; noSelectingScroll(); - _selCount = 0; - _topBar->showSelected(0); + _nonEmptySelection = false; + _topBar->showSelected(Window::TopBarWidget::SelectedState {}); App::hoveredItem(nullptr); App::pressedItem(nullptr); @@ -5864,7 +5864,7 @@ void HistoryWidget::deleteSelectedItems(bool forEveryone) { } void HistoryWidget::onListEscapePressed() { - if (_selCount && _list) { + if (_nonEmptySelection && _list) { onClearSelected(); } else { onCancel(); @@ -5915,18 +5915,17 @@ void HistoryWidget::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { void HistoryWidget::updateTopBarSelection() { if (!_list) { - _topBar->showSelected(0); + _topBar->showSelected(Window::TopBarWidget::SelectedState {}); return; } - int32 selectedForForward, selectedForDelete; - _list->getSelectionState(selectedForForward, selectedForDelete); - _selCount = selectedForForward ? selectedForForward : selectedForDelete; - _topBar->showSelected(_selCount > 0 ? _selCount : 0, (selectedForDelete == selectedForForward)); + auto selectedState = _list->getSelectionState(); + _nonEmptySelection = (selectedState.count > 0) || selectedState.textSelected; + _topBar->showSelected(selectedState); updateControlsVisibility(); updateListSize(); if (!Ui::isLayerShown() && !App::passcoded()) { - if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { + if (_nonEmptySelection || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { _list->setFocus(); } else { _field->setFocus(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index e8141307bd..a04f23378e 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -794,7 +794,7 @@ private: DragState _attachDrag = DragStateNone; object_ptr _attachDragDocument, _attachDragPhoto; - int32 _selCount; // < 0 - text selected, focus list, not _field + bool _nonEmptySelection = false; TaskQueue _fileLoader; TextUpdateEvents _textUpdateEvents = (TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index bb7b0147c3..86c8adb25c 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -127,6 +127,15 @@ QString formatDurationText(qint64 duration) { return (hours ? QString::number(hours) + ':' : QString()) + (minutes >= 10 ? QString() : QString('0')) + QString::number(minutes) + ':' + (seconds >= 10 ? QString() : QString('0')) + QString::number(seconds); } +QString formatDurationWords(qint64 duration) { + if (duration > 59) { + auto minutes = (duration / 60); + auto seconds = (duration % 60); + return lng_duration_minutes_seconds(lt_count_minutes, minutes, lt_count_seconds, seconds); + } + return lng_duration_seconds(lt_count, duration); +} + QString formatDurationAndSizeText(qint64 duration, qint64 size) { return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size)); } diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 8dece22354..9633a42b6c 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -77,6 +77,7 @@ static const int32 FileStatusSizeFailed = 0x7FFFFFF2; QString formatSizeText(qint64 size); QString formatDownloadText(qint64 ready, qint64 total); QString formatDurationText(qint64 duration); +QString formatDurationWords(qint64 duration); QString formatDurationAndSizeText(qint64 duration, qint64 size); QString formatGifAndSizeText(qint64 size); QString formatPlayedText(qint64 played, qint64 duration); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 607b0db8df..d6a43a5290 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -580,7 +580,12 @@ void OverviewInner::onDragExec() { QList urls; bool forwardSelected = false; if (uponSelected) { - forwardSelected = !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && !Adaptive::OneColumn(); + if (!Adaptive::OneColumn()) { + auto selectedState = getSelectionState(); + if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) { + forwardSelected = true; + } + } } else if (pressedHandler) { sel = pressedHandler->dragText(); //if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { @@ -1176,8 +1181,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } - int32 selectedForForward, selectedForDelete; - getSelectionState(selectedForForward, selectedForDelete); + auto selectedState = getSelectionState(); // -2 - has full selected items, but not over, 0 - no selection, 2 - over full selected items int32 isUponSelected = 0, hasSelected = 0; @@ -1223,8 +1227,10 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } if (isUponSelected > 1) { - _menu->addAction(lang(lng_context_forward_selected), _overview, SLOT(onForwardSelected())); - if (selectedForDelete == selectedForForward) { + if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) { + _menu->addAction(lang(lng_context_forward_selected), _overview, SLOT(onForwardSelected())); + } + if (selectedState.count > 0 && selectedState.count == selectedState.canDeleteCount) { _menu->addAction(lang(lng_context_delete_selected), base::lambda_guarded(this, [this] { _overview->confirmDeleteSelectedItems(); })); @@ -1232,7 +1238,7 @@ 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 (App::hoveredLinkItem()->toHistoryMessage()) { + if (App::hoveredLinkItem()->canForward()) { _menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true); } if (App::hoveredLinkItem()->canDelete()) { @@ -1256,8 +1262,10 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } _menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true); if (isUponSelected > 1) { - _menu->addAction(lang(lng_context_forward_selected), _overview, SLOT(onForwardSelected())); - if (selectedForDelete == selectedForForward) { + if (selectedState.count > 0 && selectedState.count == selectedState.canForwardCount) { + _menu->addAction(lang(lng_context_forward_selected), _overview, SLOT(onForwardSelected())); + } + if (selectedState.count > 0 && selectedState.count == selectedState.canDeleteCount) { _menu->addAction(lang(lng_context_delete_selected), base::lambda_guarded(this, [this] { _overview->confirmDeleteSelectedItems(); })); @@ -1265,7 +1273,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_clear_selection), _overview, SLOT(onClearSelected())); } else { if (isUponSelected != -2) { - if (App::mousedItem()->toHistoryMessage()) { + if (App::mousedItem()->canForward()) { _menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true); } if (App::mousedItem()->canDelete()) { @@ -1546,21 +1554,22 @@ void OverviewInner::onMenuDestroy(QObject *obj) { } } -void OverviewInner::getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const { - selectedForForward = selectedForDelete = 0; - for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { +Window::TopBarWidget::SelectedState OverviewInner::getSelectionState() const { + auto result = Window::TopBarWidget::SelectedState {}; + for (auto i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { if (i.value() == FullSelection) { - if (HistoryItem *item = App::histItemById(itemChannel(i.key()), itemMsgId(i.key()))) { + if (auto item = App::histItemById(itemChannel(i.key()), itemMsgId(i.key()))) { + ++result.count; + if (item->canForward()) { + ++result.canForwardCount; + } if (item->canDelete()) { - ++selectedForDelete; + ++result.canDeleteCount; } } - ++selectedForForward; } } - if (!selectedForDelete && !selectedForForward && !_selected.isEmpty()) { // text selection - selectedForForward = -1; - } + return result; } void OverviewInner::clearSelectedItems(bool onlyTextSelection) { @@ -2045,8 +2054,6 @@ MediaOverviewType OverviewWidget::type() const { } void OverviewWidget::switchType(MediaOverviewType type) { - _selCount = 0; - disconnect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); _inner->setSelectMode(false); @@ -2062,7 +2069,7 @@ void OverviewWidget::switchType(MediaOverviewType type) { _header = _header.toUpper(); noSelectingScroll(); - _topBar->showSelected(0); + _topBar->showSelected(Window::TopBarWidget::SelectedState {}); updateTopBarSelection(); scrollReset(); @@ -2086,12 +2093,10 @@ bool OverviewWidget::contentOverlapped(const QRect &globalRect) { } void OverviewWidget::updateTopBarSelection() { - int32 selectedForForward, selectedForDelete; - _inner->getSelectionState(selectedForForward, selectedForDelete); - _selCount = selectedForForward ? selectedForForward : selectedForDelete; - _inner->setSelectMode(_selCount > 0); + auto selectedState = _inner->getSelectionState(); + _inner->setSelectMode(selectedState.count > 0); if (App::main()) { - _topBar->showSelected(_selCount > 0 ? _selCount : 0, (selectedForDelete == selectedForForward)); + _topBar->showSelected(selectedState); _topBar->update(); } if (App::wnd() && !Ui::isLayerShown()) { diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 2bb2132033..fd3344d41e 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "window/section_widget.h" +#include "window/top_bar_widget.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/scroll_area.h" @@ -90,7 +91,7 @@ public: void changingMsgId(HistoryItem *row, MsgId newId); void repaintItem(const HistoryItem *msg); - void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const; + Window::TopBarWidget::SelectedState getSelectionState() const; void clearSelectedItems(bool onlyTextSelection = false); void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true); @@ -396,8 +397,6 @@ private: QTimer _scrollTimer; int32 _scrollDelta = 0; - int32 _selCount = 0; - object_ptr _topShadow; bool _inGrab = false; diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index dd07fb3595..07a7feff75 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -263,7 +263,9 @@ void TopBarWidget::updateControlsGeometry() { selectedButtonsTop += (height() - _forward->height()) / 2; _forward->moveToLeft(buttonsLeft, selectedButtonsTop); - buttonsLeft += _forward->width() + st::topBarActionSkip; + if (!_forward->isHidden()) { + buttonsLeft += _forward->width() + st::topBarActionSkip; + } _delete->moveToLeft(buttonsLeft, selectedButtonsTop); _clearSelection->moveToRight(st::topBarActionSkip, selectedButtonsTop); @@ -293,7 +295,7 @@ void TopBarWidget::showAll() { _clearSelection->show(); _delete->setVisible(_canDelete); - _forward->show(); + _forward->setVisible(_canForward); _mediaType->setVisible(App::main() ? App::main()->showMediaTypeSwitch() : false); if (historyPeer && !overviewPeer) { @@ -349,17 +351,20 @@ void TopBarWidget::updateMembersShowArea() { _membersShowArea->setGeometry(App::main()->getMembersShowAreaGeometry()); } -void TopBarWidget::showSelected(int selectedCount, bool canDelete) { - if (_selectedCount == selectedCount && _canDelete == canDelete) { +void TopBarWidget::showSelected(SelectedState state) { + auto canDelete = (state.count > 0 && state.count == state.canDeleteCount); + auto canForward = (state.count > 0 && state.count == state.canForwardCount); + if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward) { return; } - if (selectedCount == 0) { + if (state.count == 0) { // Don't change the visible buttons if the selection is cancelled. canDelete = _canDelete; + canForward = _canForward; } auto wasSelected = (_selectedCount > 0); - _selectedCount = selectedCount; + _selectedCount = state.count; if (_selectedCount > 0) { _forward->setNumbersText(_selectedCount); _delete->setNumbersText(_selectedCount); @@ -369,8 +374,9 @@ void TopBarWidget::showSelected(int selectedCount, bool canDelete) { } } auto hasSelected = (_selectedCount > 0); - if (_canDelete != canDelete) { + if (_canDelete != canDelete || _canForward != canForward) { _canDelete = canDelete; + _canForward = canForward; showAll(); } if (wasSelected != hasSelected) { diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index f16c778683..ad4d8aa154 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -39,8 +39,15 @@ class TopBarWidget : public TWidget, private base::Subscriber { public: TopBarWidget(QWidget *parent, gsl::not_null controller); + struct SelectedState { + bool textSelected = false; + int count = 0; + int canDeleteCount = 0; + int canForwardCount = 0; + }; + void showAll(); - void showSelected(int selectedCount, bool canDelete = false); + void showSelected(SelectedState state); void animationFinished(); void updateMembersShowArea(); @@ -77,6 +84,7 @@ private: PeerData *_searchInPeer = nullptr; int _selectedCount = 0; bool _canDelete = false; + bool _canForward = false; Animation _selectedShown;