From eec5b78054b796fcf19a9f3db032467f89e99621 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 31 Dec 2016 19:19:22 +0400 Subject: [PATCH] Delete for everyone checkbox added. Various bugfixes. --- Telegram/Resources/langs/lang.strings | 8 + Telegram/SourceFiles/boxes/boxes.style | 23 +-- Telegram/SourceFiles/boxes/confirmbox.cpp | 157 +++++++++++++----- Telegram/SourceFiles/boxes/confirmbox.h | 21 ++- Telegram/SourceFiles/boxes/members_box.cpp | 2 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 2 +- Telegram/SourceFiles/facades.cpp | 13 +- Telegram/SourceFiles/history.cpp | 96 ++++++----- Telegram/SourceFiles/history/history_item.cpp | 12 ++ Telegram/SourceFiles/history/history_item.h | 1 + Telegram/SourceFiles/historywidget.cpp | 15 +- Telegram/SourceFiles/mainwidget.cpp | 64 ++++--- Telegram/SourceFiles/mainwidget.h | 3 +- Telegram/SourceFiles/mainwindow.cpp | 46 +---- Telegram/SourceFiles/mainwindow.h | 8 - .../SourceFiles/media/view/mediaview.style | 2 +- Telegram/SourceFiles/mediaview.cpp | 18 +- Telegram/SourceFiles/mediaview.h | 3 +- Telegram/SourceFiles/overviewwidget.cpp | 17 +- .../platform/mac/main_window_mac.h | 4 +- Telegram/SourceFiles/structs.cpp | 4 +- Telegram/SourceFiles/ui/widgets/checkbox.cpp | 41 +++-- Telegram/SourceFiles/ui/widgets/checkbox.h | 12 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 9 +- Telegram/SourceFiles/ui/widgets/labels.cpp | 1 + Telegram/SourceFiles/ui/widgets/widgets.style | 4 +- Telegram/SourceFiles/window/main_window.cpp | 69 +++++++- Telegram/SourceFiles/window/main_window.h | 19 ++- 28 files changed, 414 insertions(+), 260 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a1479d84e7..fda096e052 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -959,6 +959,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_selected_delete_sure_this" = "Do you want to delete this message?"; "lng_selected_delete_sure" = "Do you want to delete {count:_not_used_|# message|# messages}?"; "lng_delete_photo_sure" = "Do you want to delete this photo?"; +"lng_delete_for_everyone_this_hint" = "This will delete it for everyone in this chat."; +"lng_delete_for_everyone_hint" = "This will delete {count:_not_used_|it|them} for everyone in this chat."; +"lng_delete_for_me_chat_this_hint" = "This will delete it just for you, not for other participants of the chat."; +"lng_delete_for_me_chat_hint" = "This will delete {count:_not_used_|it|them} just for you, not for other participants of the chat."; +"lng_delete_for_me_this_hint" = "This will delete it just for you."; +"lng_delete_for_me_hint" = "This will delete {count:_not_used_|it|them} just for you."; +"lng_delete_for_everyone_check" = "Delete for everyone"; +"lng_delete_for_other_check" = "Delete for {user}"; "lng_box_delete" = "Delete"; "lng_box_leave" = "Leave"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 80f83f51ea..dcac4225e7 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -33,6 +33,16 @@ defaultBoxButton: RoundButton(defaultLightButton) { font: boxButtonFont; } +boxTextStyle: TextStyle(defaultTextStyle) { + font: font(boxFontSize); + linkFont: font(boxFontSize); + linkFontOver: font(boxFontSize underline); +} + +boxLabelStyle: TextStyle(boxTextStyle) { + lineHeight: 22px; +} + attentionBoxButton: RoundButton(defaultBoxButton) { textFg: attentionButtonFg; textFgOver: attentionButtonFgOver; @@ -46,7 +56,7 @@ attentionBoxButton: RoundButton(defaultBoxButton) { defaultBoxCheckbox: Checkbox(defaultCheckbox) { width: -46px; textPosition: point(34px, 1px); - font: boxTextFont; + style: boxTextStyle; } boxRoundShadow: Shadow { @@ -108,12 +118,9 @@ boxMediumSkip: 20px; boxButtonPadding: margins(8px, 12px, 13px, 12px); boxLayerButtonPadding: margins(8px, 8px, 8px, 8px); boxLabel: FlatLabel(defaultFlatLabel) { + width: 285px; align: align(topleft); - style: TextStyle(defaultTextStyle) { - font: font(boxFontSize); - linkFont: font(boxFontSize); - linkFontOver: font(boxFontSize underline); - } + style: boxLabelStyle; } countryRowHeight: 36px; @@ -131,10 +138,6 @@ countriesScroll: ScrollArea(boxLayerScroll) { deltab: 3px; } -boxTextStyle: TextStyle(defaultTextStyle) { - lineHeight: 22px; -} - boxPhotoTitleFont: font(16px semibold); boxPhotoTitlePosition: point(28px, 20px); boxPhotoPadding: margins(28px, 28px, 28px, 18px); diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 2ac6144848..14e31f6473 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -112,7 +112,7 @@ base::lambda ConfirmBox::generateInformCallback(const base::lambda_copy< } void ConfirmBox::init(const QString &text) { - _text.setText(st::boxTextStyle, text, _informative ? _confirmBoxTextOptions : _textPlainOptions); + _text.setText(st::boxLabelStyle, text, _informative ? _confirmBoxTextOptions : _textPlainOptions); } void ConfirmBox::prepare() { @@ -125,7 +125,7 @@ void ConfirmBox::prepare() { void ConfirmBox::textUpdated() { _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); - _textHeight = qMin(_text.countHeight(_textWidth), 16 * int(st::boxTextStyle.lineHeight)); + _textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxLabelStyle.lineHeight); setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxPadding.bottom()); setMouseTracking(_text.hasLinks()); @@ -219,7 +219,7 @@ InformBox::InformBox(QWidget*, const QString &text, const QString &doneText, bas } MaxInviteBox::MaxInviteBox(QWidget*, const QString &link) -: _text(st::boxTextStyle, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) +: _text(st::boxLabelStyle, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) , _link(link) { } @@ -229,7 +229,7 @@ void MaxInviteBox::prepare() { addButton(lang(lng_box_ok), [this] { closeBox(); }); _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); - _textHeight = qMin(_text.countHeight(_textWidth), 16 * int(st::boxTextStyle.lineHeight)); + _textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxLabelStyle.lineHeight); setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxTextFont->height + st::boxTextFont->height * 2 + st::newGroupLinkPadding.bottom()); } @@ -302,8 +302,8 @@ void ConvertToSupergroupBox::prepare() { addButton(lang(lng_profile_convert_confirm), [this] { convertToSupergroup(); }); addButton(lang(lng_cancel), [this] { closeBox(); }); - _text.setText(st::boxTextStyle, text.join('\n'), _confirmBoxTextOptions); - _note.setText(st::boxTextStyle, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions); + _text.setText(st::boxLabelStyle, text.join('\n'), _confirmBoxTextOptions); + _note.setText(st::boxLabelStyle, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions); _textWidth = st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = _text.countHeight(_textWidth); setDimensions(st::boxWideWidth, _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth)); @@ -408,54 +408,135 @@ bool PinMessageBox::pinFail(const RPCError &error) { return true; } -RichDeleteMessageBox::RichDeleteMessageBox(QWidget*, ChannelData *channel, UserData *from, MsgId msgId) -: _channel(channel) -, _from(from) -, _msgId(msgId) -, _text(this, lang(lng_selected_delete_sure_this), Ui::FlatLabel::InitType::Simple, st::boxLabel) -, _banUser(this, lang(lng_ban_user), false, st::defaultBoxCheckbox) -, _reportSpam(this, lang(lng_report_spam), false, st::defaultBoxCheckbox) -, _deleteAll(this, lang(lng_delete_all_from), false, st::defaultBoxCheckbox) { +DeleteMessagesBox::DeleteMessagesBox(QWidget*, HistoryItem *item, bool suggestModerateActions) : _singleItem(true) { + _ids.push_back(item->fullId()); + if (suggestModerateActions && item->suggestBanReportDeleteAll()) { + _moderateFrom = item->from()->asUser(); + _moderateInChannel = item->history()->peer->asChannel(); + } } -void RichDeleteMessageBox::prepare() { - t_assert(_channel != nullptr); +DeleteMessagesBox::DeleteMessagesBox(QWidget*, const SelectedItemSet &selected) { + auto count = selected.size(); + t_assert(count > 0); + _ids.reserve(count); + for_const (auto item, selected) { + _ids.push_back(item->fullId()); + } +} + +void DeleteMessagesBox::prepare() { + auto text = QString(); + if (_moderateFrom) { + t_assert(_moderateInChannel != nullptr); + text = lang(lng_selected_delete_sure_this); + _banUser.create(this, lang(lng_ban_user), false, st::defaultBoxCheckbox); + _reportSpam.create(this, lang(lng_report_spam), false, st::defaultBoxCheckbox); + _deleteAll.create(this, lang(lng_delete_all_from), false, st::defaultBoxCheckbox); + } else { + text = _singleItem ? lang(lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, _ids.size()); + auto canDeleteAllForEveryone = true; + auto now = ::date(unixtime()); + auto deleteForUser = (UserData*)nullptr; + auto peer = (PeerData*)nullptr; + auto forEveryoneText = lang(lng_delete_for_everyone_check); + for_const (auto fullId, _ids) { + if (auto item = App::histItemById(fullId)) { + peer = item->history()->peer; + if (!item->canDeleteForEveryone(now)) { + canDeleteAllForEveryone = false; + break; + } else if (auto user = item->history()->peer->asUser()) { + if (!deleteForUser || deleteForUser == user) { + deleteForUser = user; + forEveryoneText = lng_delete_for_other_check(lt_user, user->firstName); + } else { + forEveryoneText = lang(lng_delete_for_everyone_check); + } + } + } else { + canDeleteAllForEveryone = false; + } + } + if (canDeleteAllForEveryone) { + _forEveryone.create(this, forEveryoneText, false, st::defaultBoxCheckbox); + } else if (peer && peer->isChannel()) { + if (peer->isMegagroup()) { + text += qsl("\n\n") + (_singleItem ? lang(lng_delete_for_everyone_this_hint) : lng_delete_for_everyone_hint(lt_count, _ids.size())); + } + } else if (peer->isChat()) { + text += qsl("\n\n") + (_singleItem ? lang(lng_delete_for_me_chat_this_hint) : lng_delete_for_me_chat_hint(lt_count, _ids.size())); + } else { + text += qsl("\n\n") + (_singleItem ? lang(lng_delete_for_me_this_hint) : lng_delete_for_me_hint(lt_count, _ids.size())); + } + } + _text.create(this, text, Ui::FlatLabel::InitType::Simple, st::boxLabel); addButton(lang(lng_box_delete), [this] { deleteAndClear(); }); addButton(lang(lng_cancel), [this] { closeBox(); }); _text->resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); - setDimensions(st::boxWidth, st::boxPadding.top() + _text->height() + st::boxMediumSkip + _banUser->heightNoMargins() + st::boxLittleSkip + _reportSpam->heightNoMargins() + st::boxLittleSkip + _deleteAll->heightNoMargins() + st::boxPadding.bottom()); + auto fullHeight = st::boxPadding.top() + _text->height() + st::boxPadding.bottom(); + if (_moderateFrom) { + fullHeight += st::boxMediumSkip + _banUser->heightNoMargins() + st::boxLittleSkip + _reportSpam->heightNoMargins() + st::boxLittleSkip + _deleteAll->heightNoMargins(); + } else if (_forEveryone) { + fullHeight += st::boxMediumSkip + _forEveryone->heightNoMargins(); + } + setDimensions(st::boxWidth, fullHeight); } -void RichDeleteMessageBox::resizeEvent(QResizeEvent *e) { +void DeleteMessagesBox::resizeEvent(QResizeEvent *e) { BoxContent::resizeEvent(e); _text->moveToLeft(st::boxPadding.left(), st::boxPadding.top()); - _banUser->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip); - _reportSpam->moveToLeft(st::boxPadding.left(), _banUser->bottomNoMargins() + st::boxLittleSkip); - _deleteAll->moveToLeft(st::boxPadding.left(), _reportSpam->bottomNoMargins() + st::boxLittleSkip); + if (_moderateFrom) { + _banUser->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip); + _reportSpam->moveToLeft(st::boxPadding.left(), _banUser->bottomNoMargins() + st::boxLittleSkip); + _deleteAll->moveToLeft(st::boxPadding.left(), _reportSpam->bottomNoMargins() + st::boxLittleSkip); + } else if (_forEveryone) { + _forEveryone->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip); + } } -void RichDeleteMessageBox::deleteAndClear() { - if (_banUser->checked()) { - MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _from->inputUser, MTP_boolTrue()), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); +void DeleteMessagesBox::deleteAndClear() { + if (!App::main()) { + return; } - if (_reportSpam->checked()) { - MTP::send(MTPchannels_ReportSpam(_channel->inputChannel, _from->inputUser, MTP_vector(1, MTP_int(_msgId)))); - } - if (_deleteAll->checked()) { - App::main()->deleteAllFromUser(_channel, _from); - } - if (auto item = App::histItemById(_channel ? peerToChannel(_channel->id) : 0, _msgId)) { - auto wasLast = (item->history()->lastMsg == item); - item->destroy(); - if (_msgId > 0) { - auto forEveryone = true; - App::main()->deleteMessages(_channel, QVector(1, MTP_int(_msgId)), forEveryone); - } else if (wasLast) { - App::main()->checkPeerHistory(_channel); + if (_moderateFrom) { + if (_banUser->checked()) { + MTP::send(MTPchannels_KickFromChannel(_moderateInChannel->inputChannel, _moderateFrom->inputUser, MTP_boolTrue()), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); } + if (_reportSpam->checked()) { + MTP::send(MTPchannels_ReportSpam(_moderateInChannel->inputChannel, _moderateFrom->inputUser, MTP_vector(1, MTP_int(_ids[0].msg)))); + } + if (_deleteAll->checked()) { + App::main()->deleteAllFromUser(_moderateInChannel, _moderateFrom); + } + } + + if (!_singleItem) { + App::main()->clearSelectedItems(); + } + + QMap> idsByPeer; + for_const (auto fullId, _ids) { + if (auto item = App::histItemById(fullId)) { + auto history = item->history(); + auto wasOnServer = (item->id > 0); + auto wasLast = (history->lastMsg == item); + item->destroy(); + + if (wasOnServer) { + idsByPeer[history->peer].push_back(MTP_int(fullId.msg)); + } else if (wasLast) { + App::main()->checkPeerHistory(history->peer); + } + } + } + + auto forEveryone = _forEveryone ? _forEveryone->checked() : false; + for (auto i = idsByPeer.cbegin(), e = idsByPeer.cend(); i != e; ++i) { + App::main()->deleteMessages(i.key(), i.value(), forEveryone); } Ui::hideLayer(); } diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 068577d6a3..11748b0913 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -170,9 +170,10 @@ private: }; -class RichDeleteMessageBox : public BoxContent, public RPCSender { +class DeleteMessagesBox : public BoxContent, public RPCSender { public: - RichDeleteMessageBox(QWidget*, ChannelData *channel, UserData *from, MsgId msgId); + DeleteMessagesBox(QWidget*, HistoryItem *item, bool suggestModerateActions); + DeleteMessagesBox(QWidget*, const SelectedItemSet &selected); protected: void prepare() override; @@ -182,14 +183,16 @@ protected: private: void deleteAndClear(); - ChannelData *_channel; - UserData *_from; - MsgId _msgId; + QVector _ids; + bool _singleItem = false; + UserData *_moderateFrom = nullptr; + ChannelData *_moderateInChannel = nullptr; - object_ptr _text; - object_ptr _banUser; - object_ptr _reportSpam; - object_ptr _deleteAll; + object_ptr _text = { nullptr }; + object_ptr _forEveryone = { nullptr }; + object_ptr _banUser = { nullptr }; + object_ptr _reportSpam = { nullptr }; + object_ptr _deleteAll = { nullptr }; }; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index daccc73fb0..ccd91902b9 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -374,7 +374,7 @@ void MembersBox::Inner::refresh() { resize(width(), st::membersMarginTop + st::noContactsHeight + st::membersMarginBottom); _aboutHeight = 0; } else { - _about.setText(st::boxTextStyle, lng_channel_only_last_shown(lt_count, _rows.size())); + _about.setText(st::boxLabelStyle, lng_channel_only_last_shown(lt_count, _rows.size())); _aboutHeight = st::membersAboutLimitPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutLimitPadding.bottom(); if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { _aboutHeight = 0; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index d41b9a8904..809ee84bdd 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -148,7 +148,7 @@ StickersBox::StickersBox(QWidget*, const Stickers::Order &archivedIds) : _section(Section::ArchivedPart) , _archived(0, this, archivedIds) , _aboutWidth(st::boxWideWidth - 2 * st::stickersReorderPadding.top()) -, _about(st::boxTextStyle, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { +, _about(st::boxLabelStyle, lang(lng_stickers_packs_archived), _defaultOptions, _aboutWidth) { } void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result) { diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 0aa9f987e7..5e25ab95f1 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -316,18 +316,7 @@ PeerData *getPeerForMouseAction() { bool hideWindowNoQuit() { if (!App::quitting()) { if (auto w = App::wnd()) { - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { - if (w->minimizeToTray()) { - Ui::showChatsList(); - return true; - } - } else if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { - w->closeWithoutDestroy(); - w->updateIsActive(Global::OfflineBlurTimeout()); - w->updateGlobalMenu(); - Ui::showChatsList(); - return true; - } + w->hideNoQuit(); } } return false; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 567cb926fb..e1bc12fbf8 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -48,6 +48,14 @@ constexpr int kSetMyActionForMs = 10000; auto GlobalPinnedIndex = 0; +HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from) { + QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org"))); + EntitiesInText entities; + textParseEntities(text, _historyTextNoMonoOptions.flags, &entities); + entities.push_front(EntityInText(EntityInTextItalic, 0, text.size())); + return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, { text, entities }); +} + } // namespace void historyInit() { @@ -741,7 +749,7 @@ void Histories::clearPinned() { } HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem) { - MsgId msgId = 0; + auto msgId = MsgId(0); switch (msg.type()) { case mtpc_messageEmpty: msgId = msg.c_messageEmpty().vid.v; break; case mtpc_message: msgId = msg.c_message().vid.v; break; @@ -749,7 +757,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, } if (!msgId) return nullptr; - HistoryItem *result = App::histItemById(channelId(), msgId); + auto result = App::histItemById(channelId(), msgId); if (result) { if (!result->detached() && detachExistingItem) { result->detach(); @@ -769,37 +777,42 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, break; case mtpc_message: { - const auto &m(msg.c_message()); - int badMedia = 0; // 1 - unsupported, 2 - empty + auto &m = msg.c_message(); + enum class MediaCheckResult { + Good, + Unsupported, + Empty, + }; + auto badMedia = MediaCheckResult::Good; if (m.has_media()) switch (m.vmedia.type()) { case mtpc_messageMediaEmpty: case mtpc_messageMediaContact: break; case mtpc_messageMediaGeo: switch (m.vmedia.c_messageMediaGeo().vgeo.type()) { case mtpc_geoPoint: break; - case mtpc_geoPointEmpty: badMedia = 2; break; - default: badMedia = 1; break; + case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; } break; case mtpc_messageMediaVenue: switch (m.vmedia.c_messageMediaVenue().vgeo.type()) { case mtpc_geoPoint: break; - case mtpc_geoPointEmpty: badMedia = 2; break; - default: badMedia = 1; break; + case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; } break; case mtpc_messageMediaPhoto: switch (m.vmedia.c_messageMediaPhoto().vphoto.type()) { case mtpc_photo: break; - case mtpc_photoEmpty: badMedia = 2; break; - default: badMedia = 1; break; + case mtpc_photoEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; } break; case mtpc_messageMediaDocument: switch (m.vmedia.c_messageMediaDocument().vdocument.type()) { case mtpc_document: break; - case mtpc_documentEmpty: badMedia = 2; break; - default: badMedia = 1; break; + case mtpc_documentEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; } break; case mtpc_messageMediaWebPage: @@ -808,25 +821,21 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, case mtpc_webPageEmpty: case mtpc_webPagePending: break; case mtpc_webPageNotModified: - default: badMedia = 1; break; + default: badMedia = MediaCheckResult::Unsupported; break; } break; case mtpc_messageMediaGame: switch (m.vmedia.c_messageMediaGame().vgame.type()) { case mtpc_game: break; - default: badMedia = 1; break; + default: badMedia = MediaCheckResult::Unsupported; break; } break; case mtpc_messageMediaUnsupported: - default: badMedia = 1; break; + default: badMedia = MediaCheckResult::Unsupported; break; } - if (badMedia == 1) { - QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org"))); - EntitiesInText entities; - textParseEntities(text, _historyTextNoMonoOptions.flags, &entities); - entities.push_front(EntityInText(EntityInTextItalic, 0, text.size())); - result = HistoryMessage::create(this, m.vid.v, m.vflags.v, m.vreply_to_msg_id.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v, { text, entities }); - } else if (badMedia) { + if (badMedia == MediaCheckResult::Unsupported) { + result = createUnsupportedMessage(this, m.vid.v, m.vflags.v, m.vreply_to_msg_id.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v); + } else if (badMedia == MediaCheckResult::Empty) { result = HistoryService::create(this, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0); } else { result = HistoryMessage::create(this, m); @@ -834,18 +843,23 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, } break; case mtpc_messageService: { - const auto &d(msg.c_messageService()); - result = HistoryService::create(this, d); + auto &m = msg.c_messageService(); + if (m.vaction.type() == mtpc_messageActionPhoneCall) { + auto viaBotId = 0; + result = createUnsupportedMessage(this, m.vid.v, mtpCastFlags(m.vflags.v), m.vreply_to_msg_id.v, viaBotId, date(m.vdate), m.vfrom_id.v); + } else { + result = HistoryService::create(this, m); + } if (applyServiceAction) { - const auto &action(d.vaction); - switch (d.vaction.type()) { + auto &action = m.vaction; + switch (action.type()) { case mtpc_messageActionChatAddUser: { - const auto &d(action.c_messageActionChatAddUser()); + auto &d = action.c_messageActionChatAddUser(); if (peer->isMegagroup()) { - const auto &v(d.vusers.c_vector().v); - for (int32 i = 0, l = v.size(); i < l; ++i) { - if (UserData *user = App::userLoaded(peerFromUser(v.at(i)))) { + auto &v = d.vusers.c_vector().v; + for (auto i = 0, l = v.size(); i != l; ++i) { + if (auto user = App::userLoaded(peerFromUser(v[i]))) { if (peer->asChannel()->mgInfo->lastParticipants.indexOf(user) < 0) { peer->asChannel()->mgInfo->lastParticipants.push_front(user); peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated; @@ -863,7 +877,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, } break; case mtpc_messageActionChatJoinedByLink: { - const auto &d(action.c_messageActionChatJoinedByLink()); + auto &d = action.c_messageActionChatJoinedByLink(); if (peer->isMegagroup()) { if (result->from()->isUser()) { if (peer->asChannel()->mgInfo->lastParticipants.indexOf(result->from()->asUser()) < 0) { @@ -881,13 +895,13 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, } break; case mtpc_messageActionChatDeletePhoto: { - ChatData *chat = peer->asChat(); + auto chat = peer->asChat(); if (chat) chat->setPhoto(MTP_chatPhotoEmpty()); } break; case mtpc_messageActionChatDeleteUser: { - const auto &d(action.c_messageActionChatDeleteUser()); - PeerId uid = peerFromUser(d.vuser_id); + auto &d = action.c_messageActionChatDeleteUser(); + auto uid = peerFromUser(d.vuser_id); if (lastKeyboardFrom == uid) { clearLastKeyboard(); } @@ -952,7 +966,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, } break; case mtpc_messageActionChatEditTitle: { - auto &d(action.c_messageActionChatEditTitle()); + auto &d = action.c_messageActionChatEditTitle(); if (auto chat = peer->asChat()) { chat->setName(qs(d.vtitle)); } @@ -961,18 +975,18 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, case mtpc_messageActionChatMigrateTo: { peer->asChat()->flags |= MTPDchat::Flag::f_deactivated; - //const auto &d(action.c_messageActionChatMigrateTo()); - //PeerData *channel = App::channelLoaded(d.vchannel_id.v); + //auto &d = action.c_messageActionChatMigrateTo(); + //auto channel = App::channelLoaded(d.vchannel_id.v); } break; case mtpc_messageActionChannelMigrateFrom: { - //const auto &d(action.c_messageActionChannelMigrateFrom()); - //PeerData *chat = App::chatLoaded(d.vchat_id.v); + //auto &d = action.c_messageActionChannelMigrateFrom(); + //auto chat = App::chatLoaded(d.vchat_id.v); } break; case mtpc_messageActionPinMessage: { - if (d.has_reply_to_msg_id() && result && result->history()->peer->isMegagroup()) { - result->history()->peer->asChannel()->mgInfo->pinnedMsgId = d.vreply_to_msg_id.v; + if (m.has_reply_to_msg_id() && result && result->history()->peer->isMegagroup()) { + result->history()->peer->asChannel()->mgInfo->pinnedMsgId = m.vreply_to_msg_id.v; if (App::main()) emit App::main()->peerUpdated(result->history()->peer); } } break; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 755db6adcb..ed487cd526 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -772,6 +772,18 @@ bool HistoryItem::canEdit(const QDateTime &cur) const { return false; } +bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const { + auto messageToMyself = (peerToUser(_history->peer->id) == MTP::authedId()); + auto messageTooOld = messageToMyself ? false : (date.secsTo(cur) >= Global::EditTimeLimit()); + if (id < 0 || messageToMyself || messageTooOld) return false; + if (history()->peer->isChannel()) return false; + + if (auto msg = toHistoryMessage()) { + return !isPost() && out(); + } + return false; +} + bool HistoryItem::unread() const { // Messages from myself are always read. if (history()->peer->isSelf()) return false; diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 15ca09084b..77745d36e3 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -704,6 +704,7 @@ public: } bool canEdit(const QDateTime &cur) const; + bool canDeleteForEveryone(const QDateTime &cur) const; bool suggestBanReportDeleteAll() const { ChannelData *channel = history()->peer->asChannel(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index f8c1f875d6..1e14a55cca 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2848,7 +2848,7 @@ void HistoryHider::resizeEvent(QResizeEvent *e) { _send->show(); _cancel->show(); } - h += st::boxTopMargin + qMax(st::boxTextFont->height, st::boxTextStyle.lineHeight) + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom(); + h += st::boxTopMargin + qMax(st::boxTextFont->height, st::boxLabelStyle.lineHeight) + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom(); } else { h += st::historyForwardChooseFont->height; _send->hide(); @@ -2862,7 +2862,7 @@ void HistoryHider::resizeEvent(QResizeEvent *e) { bool HistoryHider::offerPeer(PeerId peer) { if (!peer) { _offered = nullptr; - _toText.setText(st::boxTextStyle, QString()); + _toText.setText(st::boxLabelStyle, QString()); _toTextWidth = 0; resizeEvent(nullptr); return false; @@ -2902,7 +2902,7 @@ bool HistoryHider::offerPeer(PeerId peer) { return false; } - _toText.setText(st::boxTextStyle, phrase, _textNameOptions); + _toText.setText(st::boxLabelStyle, phrase, _textNameOptions); _toTextWidth = _toText.maxWidth(); if (_toTextWidth > _box.width() - st::boxPadding.left() - st::boxLayerButtonPadding.right()) { _toTextWidth = _box.width() - st::boxPadding.left() - st::boxLayerButtonPadding.right(); @@ -8352,8 +8352,13 @@ void HistoryWidget::confirmDeleteContextItem() { auto item = App::contextItem(); if (!item || item->type() != HistoryItemMsg) return; - auto message = item->toHistoryMessage(); - App::main()->deleteLayer((message && message->uploading()) ? -2 : -1); + if (auto message = item->toHistoryMessage()) { + if (message->uploading()) { + App::main()->cancelUploadLayer(); + return; + } + } + App::main()->deleteLayer(); } void HistoryWidget::confirmDeleteSelectedItems() { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 86202296c0..d7d3c3a8e4 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -660,43 +660,39 @@ void MainWidget::forwardLayer(int forwardSelected) { } void MainWidget::deleteLayer(int selectedCount) { - if (selectedCount == -1 && !_overview) { - if (auto item = App::contextItem()) { - if (item->suggestBanReportDeleteAll()) { - Ui::show(Box(item->history()->peer->asChannel(), item->from()->asUser(), item->id)); - return; - } - } - } - auto text = (selectedCount < 0) ? lang(selectedCount < -1 ? lng_selected_cancel_sure_this : lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, selectedCount); - auto confirmText = lang((selectedCount < -1) ? lng_selected_upload_stop : lng_box_delete); - auto cancelText = lang((selectedCount < -1) ? lng_continue : lng_cancel); - if (selectedCount < -1) { - if (auto item = App::contextItem()) { - App::uploader()->pause(item->fullId()); - } - } - Ui::show(Box(text, confirmText, cancelText, base::lambda_guarded(this, [this, selectedCount] { - if (selectedCount < 0) { - if (_overview) { - _overview->deleteContextItem(false); - } else { - _history->deleteContextItem(false); - } - if (selectedCount < -1) { - App::uploader()->unpause(); - } + if (selectedCount) { + auto forDelete = true; + SelectedItemSet selected; + if (_overview) { + _overview->fillSelectedItems(selected, forDelete); } else { - if (_overview) { - _overview->deleteSelectedItems(false); - } else { - _history->deleteSelectedItems(false); - } + _history->fillSelectedItems(selected, forDelete); } - }), base::lambda_guarded(this, [selectedCount] { - if (selectedCount < -1) { - App::uploader()->unpause(); + if (!selected.isEmpty()) { + Ui::show(Box(selected)); } + } else if (auto item = App::contextItem()) { + auto suggestModerateActions = !_overview; + Ui::show(Box(item, suggestModerateActions)); + } +} + +void MainWidget::cancelUploadLayer() { + auto item = App::contextItem(); + if (!item) { + return; + } + + App::uploader()->pause(item->fullId()); + Ui::show(Box(lang(lng_selected_cancel_sure_this), lang(lng_selected_upload_stop), lang(lng_continue), base::lambda_guarded(this, [this] { + if (_overview) { + _overview->deleteContextItem(false); + } else { + _history->deleteContextItem(false); + } + App::uploader()->unpause(); + }), base::lambda_guarded(this, [] { + App::uploader()->unpause(); }))); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 7191803ea9..f857ee058f 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -228,7 +228,8 @@ public: int32 dlgsWidth() const; void forwardLayer(int forwardSelected = 0); // -1 - send paths - void deleteLayer(int selectedCount = -1); // -1 - context item, else selected, -2 - cancel upload + void deleteLayer(int selectedCount = 0); // 0 - context item + void cancelUploadLayer(); void shareContactLayer(UserData *contact); void shareUrlLayer(const QString &url, const QString &text); void inlineSwitchLayer(const QString &botAndQuery); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 47810b36fa..750ba32fa4 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -204,11 +204,7 @@ void MainWindow::firstShow() { psFirstShow(); updateTrayMenu(); - _mediaView = new MediaView(); -} - -QWidget *MainWindow::filedialogParent() { - return (_mediaView && _mediaView->isVisible()) ? (QWidget*)_mediaView : (QWidget*)this; + createMediaView(); } void MainWindow::clearWidgets() { @@ -600,30 +596,6 @@ void MainWindow::layerHidden() { checkHistoryActivation(); } -void MainWindow::onReActivate() { - if (auto w = App::wnd()) { - if (auto f = QApplication::focusWidget()) { - f->clearFocus(); - } - w->windowHandle()->requestActivate(); - w->activate(); - if (auto f = QApplication::focusWidget()) { - f->clearFocus(); - } - w->setInnerFocus(); - } -} - -void MainWindow::hideMediaview() { - if (_mediaView && !_mediaView->isHidden()) { - _mediaView->hide(); -#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 - onReActivate(); - QTimer::singleShot(200, this, SLOT(onReActivate())); -#endif - } -} - bool MainWindow::contentOverlapped(const QRect &globalRect) { if (_main && _main->contentOverlapped(globalRect)) return true; if (_layerBg && _layerBg->contentOverlapped(globalRect)) return true; @@ -712,21 +684,6 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { return Platform::MainWindow::eventFilter(obj, e); } -bool MainWindow::minimizeToTray() { - if (App::quitting() || !psHasTrayIcon()) return false; - - closeWithoutDestroy(); - if (cPlatform() == dbipWindows && trayIcon && !cSeenTrayTooltip()) { - trayIcon->showMessage(str_const_toString(AppName), lang(lng_tray_icon_text), QSystemTrayIcon::Information, 10000); - cSetSeenTrayTooltip(true); - Local::writeSettings(); - } - updateIsActive(Global::OfflineBlurTimeout()); - updateTrayMenu(); - updateGlobalMenu(); - return true; -} - void MainWindow::updateTrayMenu(bool force) { if (!trayIconMenu || (cPlatform() == dbipWindows && !force)) return; @@ -1450,7 +1407,6 @@ MainWindow::~MainWindow() { _clearManager->stop(); _clearManager = nullptr; } - delete _mediaView; delete trayIcon; delete trayIconMenu; } diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 41535c0ba8..93a09a63bb 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -80,8 +80,6 @@ public: void firstShow(); - QWidget *filedialogParent(); - void inactivePress(bool inactive); bool inactivePress() const; @@ -142,7 +140,6 @@ public: void changingMsgId(HistoryItem *row, MsgId newId); bool isActive(bool cached = true) const; - void hideMediaview(); QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) override; @@ -184,7 +181,6 @@ public slots: void quitFromTray(); void showFromTray(QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Unknown); - bool minimizeToTray(); void toggleTray(QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Unknown); void toggleDisplayNotifyFromTray(); @@ -202,8 +198,6 @@ public slots: void onLogout(); void updateGlobalMenu(); // for OS X top menu - void onReActivate(); - void app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); signals: @@ -283,8 +277,6 @@ private: using NotifyWhenAlerts = QMap; NotifyWhenAlerts _notifyWhenAlerts; - MediaView *_mediaView = nullptr; - }; class PreLaunchWindow : public TWidget { diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 0fd201b437..abbc54e83b 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -200,7 +200,7 @@ mediaviewCaptionRadius: 2px; themePreviewSize: size(903px, 584px); themePreviewBg: windowBg; -themePreviewOverlayOpacity: 0.7; +themePreviewOverlayOpacity: 0.8; themePreviewMargin: margins(36px, 52px, 36px, 88px); themePreviewTitleTop: 14px; themePreviewTitleFg: windowBoldFg; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 21f67356fa..b907f96869 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -67,7 +67,7 @@ bool typeHasMediaOverview(MediaOverviewType type) { } // namespace -MediaView::MediaView() : TWidget(App::wnd()) +MediaView::MediaView(QWidget*) : TWidget(nullptr) , _animStarted(getms()) , _docDownload(this, lang(lng_media_download), st::mediaviewFileLink) , _docSaveAs(this, lang(lng_mediaview_save_as), st::mediaviewFileLink) @@ -75,7 +75,8 @@ MediaView::MediaView() : TWidget(App::wnd()) , _radial(animation(this, &MediaView::step_radial)) , _lastAction(-st::mediaviewDeltaFromLastAction, -st::mediaviewDeltaFromLastAction) , _a_state(animation(this, &MediaView::step_state)) -, _dropdown(this, st::mediaviewDropdownMenu) { +, _dropdown(this, st::mediaviewDropdownMenu) +, _dropdownShowTimer(this) { TextCustomTagsMap custom; custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); _saveMsgText.setRichText(st::mediaviewSaveMsgStyle, lang(lng_mediaview_saved), _textDlgOptions, custom); @@ -121,6 +122,8 @@ MediaView::MediaView() : TWidget(App::wnd()) connect(_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); _dropdown->setHiddenCallback([this] { dropdownHidden(); }); + _dropdownShowTimer->setSingleShot(true); + connect(_dropdownShowTimer, SIGNAL(timeout()), this, SLOT(onDropdown())); } void MediaView::moveToScreen() { @@ -1333,8 +1336,9 @@ void MediaView::updateThemePreviewGeometry() { auto previewRect = QRect((width() - st::themePreviewSize.width()) / 2, (height() - st::themePreviewSize.height()) / 2, st::themePreviewSize.width(), st::themePreviewSize.height()); _themePreviewRect = previewRect.marginsAdded(st::themePreviewMargin); if (_themeApply) { - auto right = width() - _themePreviewRect.x() - _themePreviewRect.width() + st::themePreviewMargin.right(); - _themeApply->moveToRight(right, _themePreviewRect.y() + _themePreviewRect.height() - st::themePreviewMargin.bottom() + (st::themePreviewMargin.bottom() - _themeApply->height()) / 2); + auto right = qMax(width() - _themePreviewRect.x() - _themePreviewRect.width(), 0) + st::themePreviewMargin.right(); + auto bottom = qMin(height(), _themePreviewRect.y() + _themePreviewRect.height()); + _themeApply->moveToRight(right, bottom - st::themePreviewMargin.bottom() + (st::themePreviewMargin.bottom() - _themeApply->height()) / 2); right += _themeApply->width() + st::themePreviewButtonsSkip; _themeCancel->moveToRight(right, _themeApply->y()); } @@ -2370,7 +2374,9 @@ bool MediaView::updateOverState(OverState newState) { bool result = true; if (_over != newState) { if (newState == OverMore && !_ignoringDropdown) { - QTimer::singleShot(0, this, SLOT(onDropdown())); + _dropdownShowTimer->start(0); + } else { + _dropdownShowTimer->stop(); } updateOverRect(_over); updateOverRect(newState); @@ -2393,7 +2399,7 @@ bool MediaView::updateOverState(OverState newState) { if (i != _animOpacities.end()) { i->start(1); } else { - _animOpacities.insert(_over, anim::value()); + _animOpacities.insert(_over, anim::value(0, 1)); } if (!_a_state.animating()) _a_state.start(); } diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 60cd2a8f36..1d329aeb14 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -47,7 +47,7 @@ class MediaView : public TWidget, private base::Subscriber, public RPCSender, pu Q_OBJECT public: - MediaView(); + MediaView(QWidget*); void setVisible(bool visible) override; @@ -330,6 +330,7 @@ private: Ui::PopupMenu *_menu = nullptr; object_ptr _dropdown; + object_ptr _dropdownShowTimer; struct ActionData { QString text; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 3cb48929b6..2e71b138e4 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2276,16 +2276,21 @@ void OverviewWidget::confirmDeleteContextItem() { auto item = App::contextItem(); if (!item || item->type() != HistoryItemMsg) return; - auto message = item->toHistoryMessage(); - App::main()->deleteLayer((message && message->uploading()) ? -2 : -1); + if (auto message = item->toHistoryMessage()) { + if (message->uploading()) { + App::main()->cancelUploadLayer(); + return; + } + } + App::main()->deleteLayer(); } void OverviewWidget::confirmDeleteSelectedItems() { - SelectedItemSet sel; - _inner->fillSelectedItems(sel); - if (sel.isEmpty()) return; + SelectedItemSet selected; + _inner->fillSelectedItems(selected); + if (selected.isEmpty()) return; - App::main()->deleteLayer(sel.size()); + App::main()->deleteLayer(selected.size()); } void OverviewWidget::deleteContextItem(bool forEveryone) { diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index a9feffd740..250524b890 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -51,8 +51,6 @@ public: virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; - void closeWithoutDestroy() override; - int getCustomTitleHeight() const { return _customTitleHeight; } @@ -103,6 +101,8 @@ protected: QTimer psUpdatedPositionTimer; + void closeWithoutDestroy() override; + private: void createGlobalMenu(); void updateTitleCounter(); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 8d894744eb..0f17402e9a 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -927,7 +927,7 @@ void PhotoCancelClickHandler::onClickImpl() const { if (auto media = item->getMedia()) { if (media->type() == MediaTypePhoto && static_cast(media)->photo() == data) { App::contextItem(item); - App::main()->deleteLayer(-2); + App::main()->cancelUploadLayer(); } } } @@ -1240,7 +1240,7 @@ void DocumentCancelClickHandler::onClickImpl() const { if (auto media = item->getMedia()) { if (media->getDocument() == data) { App::contextItem(item); - App::main()->deleteLayer(-2); + App::main()->cancelUploadLayer(); } } } diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp index 12c8a54700..13a11b66e1 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -79,6 +79,13 @@ public: Radiobuttons radiobuttons; +TextParseOptions _checkboxOptions = { + TextParseMultiline, // flags + 0, // maxw + 0, // maxh + Qt::LayoutDirectionAuto, // dir +}; + } // namespace void RadiobuttonGroup::remove(Radiobutton * const &radio) { @@ -90,17 +97,11 @@ void RadiobuttonGroup::remove(Radiobutton * const &radio) { Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : RippleButton(parent, st.ripple) , _st(st) -, _text(text) -, _fullText(text) -, _textWidth(st.font->width(text)) +, _text(_st.style, text, _checkboxOptions) , _checked(checked) { if (_st.width <= 0) { - resizeToWidth(_textWidth - _st.width); + resizeToWidth(_text.maxWidth() - _st.width); } else { - if (_st.width < _st.textPosition.x() + _textWidth + (_st.textPosition.x() - _st.diameter)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1)); - _textWidth = _st.font->width(_text); - } resizeToWidth(_st.width); } _checkRect = myrtlrect(_st.margin.left(), _st.margin.top(), _st.diameter, _st.diameter); @@ -129,7 +130,7 @@ void Checkbox::finishAnimations() { } int Checkbox::naturalWidth() const { - return _st.textPosition.x() + _st.font->width(_fullText); + return _st.textPosition.x() + _text.maxWidth(); } void Checkbox::paintEvent(QPaintEvent *e) { @@ -157,9 +158,10 @@ void Checkbox::paintEvent(QPaintEvent *e) { } if (_checkRect.contains(e->rect())) return; + auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1); + p.setPen(_st.textFg); - p.setFont(_st.font); - p.drawTextLeft(_st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), width(), _text, _textWidth); + _text.drawLeftElided(p, _st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), textWidth, width()); } void Checkbox::onClicked() { @@ -195,19 +197,13 @@ QPoint Checkbox::prepareRippleStartPosition() const { Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::Checkbox &st) : RippleButton(parent, st.ripple) , _st(st) -, _text(text) -, _fullText(text) -, _textWidth(st.font->width(text)) +, _text(_st.style, text, _checkboxOptions) , _checked(checked) , _group(radiobuttons.reg(group)) , _value(value) { if (_st.width <= 0) { - resizeToWidth(_textWidth - _st.width); + resizeToWidth(_text.maxWidth() - _st.width); } else { - if (_st.width < _st.textPosition.x() + _textWidth + (_st.textPosition.x() - _st.diameter)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1)); - _textWidth = _st.font->width(_text); - } resizeToWidth(_st.width); } _checkRect = myrtlrect(_st.margin.left(), _st.margin.top(), _st.diameter, _st.diameter); @@ -235,7 +231,7 @@ void Radiobutton::setChecked(bool checked) { } int Radiobutton::naturalWidth() const { - return _st.textPosition.x() + _st.font->width(_fullText); + return _st.textPosition.x() + _text.maxWidth(); } void Radiobutton::paintEvent(QPaintEvent *e) { @@ -278,9 +274,10 @@ void Radiobutton::paintEvent(QPaintEvent *e) { } if (_checkRect.contains(e->rect())) return; + auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1); + p.setPen(_st.textFg); - p.setFont(_st.font); - p.drawTextLeft(_st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), width(), _text, _textWidth); + _text.drawLeftElided(p, _st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), textWidth, width()); } void Radiobutton::onClicked() { diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index 14969278c4..1dc9a7a357 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -63,8 +63,7 @@ signals: private: const style::Checkbox &_st; - QString _text, _fullText; - int32 _textWidth; + Text _text; QRect _checkRect; bool _checked; @@ -76,12 +75,12 @@ class Radiobutton : public RippleButton { Q_OBJECT public: - Radiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked = false, const style::Checkbox &st = st::defaultCheckbox); + Radiobutton(QWidget *parent, const QString &group, int value, const QString &text, bool checked = false, const style::Checkbox &st = st::defaultCheckbox); bool checked() const; void setChecked(bool checked); - int32 val() const { + int val() const { return _value; } @@ -112,15 +111,14 @@ private: const style::Checkbox &_st; - QString _text, _fullText; - int32 _textWidth; + Text _text; QRect _checkRect; bool _checked; Animation _a_checked; void *_group; - int32 _value; + int _value; }; diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 83d343422b..1869de0a9c 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -311,7 +311,8 @@ bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) { int InnerDropdown::resizeGetHeight(int newWidth) { auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); if (auto widget = static_cast(_scroll->widget())) { - widget->resizeToWidth(newWidth - _st.padding.left() - _st.padding.right() - _st.scrollMargin.left() - _st.scrollMargin.right()); + auto containerWidth = newWidth - _st.padding.left() - _st.padding.right() - _st.scrollMargin.left() - _st.scrollMargin.right(); + widget->resizeToWidth(containerWidth); newHeight += widget->height(); } if (_maxHeight > 0) { @@ -332,7 +333,7 @@ void InnerDropdown::Container::setVisibleTopBottom(int visibleTop, int visibleBo } void InnerDropdown::Container::resizeToContent() { - auto newWidth = _st.scrollPadding.top() + _st.scrollPadding.bottom(); + auto newWidth = _st.scrollPadding.left() + _st.scrollPadding.right(); auto newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom(); if (auto child = static_cast(children().front())) { newWidth += child->width(); @@ -344,8 +345,8 @@ void InnerDropdown::Container::resizeToContent() { } int InnerDropdown::Container::resizeGetHeight(int newWidth) { - int innerWidth = newWidth - _st.scrollPadding.left() - _st.scrollPadding.right(); - int result = _st.scrollPadding.top() + _st.scrollPadding.bottom(); + auto innerWidth = newWidth - _st.scrollPadding.left() - _st.scrollPadding.right(); + auto result = _st.scrollPadding.top() + _st.scrollPadding.bottom(); if (auto child = static_cast(children().front())) { child->resizeToWidth(innerWidth); child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); diff --git a/Telegram/SourceFiles/ui/widgets/labels.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp index 1a9c788d80..9d93f4aa02 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.cpp +++ b/Telegram/SourceFiles/ui/widgets/labels.cpp @@ -34,6 +34,7 @@ TextParseOptions _labelOptions = { 0, // maxh Qt::LayoutDirectionAuto, // dir }; + TextParseOptions _labelMarkedOptions = { TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands | TextParseMono, // flags 0, // maxw diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index d85b8568e6..447d732f06 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -109,7 +109,7 @@ Checkbox { radioSkip: pixels; checkIcon: icon; - font: font; + style: TextStyle; duration: int; rippleAreaPosition: point; @@ -641,7 +641,7 @@ defaultCheckbox: Checkbox { checkIcon: defaultCheckboxIcon; radioSkip: 65px; // * 0.1 - font: normalFont; + style: defaultTextStyle; duration: 120; rippleAreaSize: 38px; diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 769b1d2aa9..58b85f739f 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_window.h" #include "platform/platform_window_title.h" #include "window/window_theme.h" +#include "mediaview.h" +#include "mainwindow.h" namespace Window { @@ -43,6 +45,57 @@ MainWindow::MainWindow() : QWidget() subscribe(Global::RefUnreadCounterUpdate(), [this] { updateUnreadCounter(); }); } +bool MainWindow::hideNoQuit() { + if (_mediaView && !_mediaView->isHidden()) { + _mediaView->hide(); + return true; + } + if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + if (minimizeToTray()) { + Ui::showChatsList(); + return true; + } + } else if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { + closeWithoutDestroy(); + updateIsActive(Global::OfflineBlurTimeout()); + updateGlobalMenu(); + Ui::showChatsList(); + return true; + } +} + +void MainWindow::hideMediaview() { + if (_mediaView && !_mediaView->isHidden()) { + _mediaView->hide(); +#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 + onReActivate(); + QTimer::singleShot(200, this, SLOT(onReActivate())); +#endif + } +} + +void MainWindow::onReActivate() { + if (auto w = App::wnd()) { + if (auto f = QApplication::focusWidget()) { + f->clearFocus(); + } + w->windowHandle()->requestActivate(); + w->activate(); + if (auto f = QApplication::focusWidget()) { + f->clearFocus(); + } + w->setInnerFocus(); + } +} + +QWidget *MainWindow::filedialogParent() { + return (_mediaView && _mediaView->isVisible()) ? (QWidget*)_mediaView : (QWidget*)this; +} + +void MainWindow::createMediaView() { + _mediaView.create(nullptr); +} + void MainWindow::init() { initHook(); @@ -197,11 +250,25 @@ void MainWindow::savePosition(Qt::WindowState state) { } } -MainWindow::~MainWindow() { +bool MainWindow::minimizeToTray() { + if (App::quitting() || !psHasTrayIcon()) return false; + + closeWithoutDestroy(); + if (cPlatform() == dbipWindows && trayIcon && !cSeenTrayTooltip()) { + trayIcon->showMessage(str_const_toString(AppName), lang(lng_tray_icon_text), QSystemTrayIcon::Information, 10000); + cSetSeenTrayTooltip(true); + Local::writeSettings(); + } + updateIsActive(Global::OfflineBlurTimeout()); + updateTrayMenu(); + updateGlobalMenu(); + return true; } void MainWindow::closeWithoutDestroy() { hide(); } +MainWindow::~MainWindow() = default; + } // namespace Window diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 50325b5165..7e586a3d71 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -22,6 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_title.h" +class MediaView; + namespace Window { class TitleWidget; @@ -32,6 +34,9 @@ class MainWindow : public QWidget, protected base::Subscriber { public: MainWindow(); + bool hideNoQuit(); + void hideMediaview(); + void init(); HitTestResult hitTest(const QPoint &p) const; @@ -46,7 +51,7 @@ public: return _titleText; } - virtual void closeWithoutDestroy(); + QWidget *filedialogParent(); virtual ~MainWindow(); @@ -54,6 +59,9 @@ public: return _body.data(); } +public slots: + bool minimizeToTray(); + protected: void resizeEvent(QResizeEvent *e) override; @@ -71,15 +79,22 @@ protected: virtual void unreadCounterChangedHook() { } + virtual void closeWithoutDestroy() { + hide(); + } + // This one is overriden in Windows for historical reasons. virtual int32 screenNameChecksum(const QString &name) const; void setPositionInited(); + void createMediaView(); + private slots: void savePositionByTimer() { savePosition(); } + void onReActivate(); private: void updatePalette(); @@ -95,6 +110,8 @@ private: QString _titleText; + object_ptr _mediaView = { nullptr }; + }; } // namespace Window