diff --git a/Telegram/Resources/icons/media_type_file.png b/Telegram/Resources/icons/media_type_file.png deleted file mode 100644 index 37c310a39e..0000000000 Binary files a/Telegram/Resources/icons/media_type_file.png and /dev/null differ diff --git a/Telegram/Resources/icons/media_type_file@2x.png b/Telegram/Resources/icons/media_type_file@2x.png deleted file mode 100644 index c1e9fa7736..0000000000 Binary files a/Telegram/Resources/icons/media_type_file@2x.png and /dev/null differ diff --git a/Telegram/Resources/icons/media_type_photo.png b/Telegram/Resources/icons/media_type_photo.png deleted file mode 100644 index 1bda90336a..0000000000 Binary files a/Telegram/Resources/icons/media_type_photo.png and /dev/null differ diff --git a/Telegram/Resources/icons/media_type_photo@2x.png b/Telegram/Resources/icons/media_type_photo@2x.png deleted file mode 100644 index 87a72b39e0..0000000000 Binary files a/Telegram/Resources/icons/media_type_photo@2x.png and /dev/null differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 49a02ec087..24f4f753cc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -829,7 +829,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_duration_and_size" = "{duration}, {size}"; "lng_duration_played" = "{played} / {duration}"; "lng_date_and_duration" = "{date}, {duration}"; -"lng_choose_images" = "Choose images"; +"lng_choose_image" = "Choose an image"; +"lng_choose_files" = "Choose files"; "lng_game_tag" = "Game"; "lng_context_view_profile" = "View profile"; @@ -876,10 +877,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_really_send_image" = "Do you want to send this image?"; "lng_really_send_file" = "Do you want to send this file?"; "lng_really_share_contact" = "Do you want to share this contact?"; -"lng_send_image_compressed" = "Send compressed image"; -"lng_send_image_empty" = "Could not send an empty file :("; -"lng_send_image_too_large" = "Could not send a file, because it is larger than 1.5 GB :("; +"lng_send_images_compress" = "Compressed {count:_not_used_|image|images}"; +"lng_send_image_non_local" = "Could not send a non local file: {name}"; +"lng_send_image_empty" = "Could not send an empty file: {name}"; +"lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}"; "lng_send_folder" = "Could not send «{name}» because it is a directory :("; +"lng_send_images_selected" = "{count:_not_used_|# image|# images} selected"; +"lng_send_photos" = "Send {count:_not_used_|# photo|# photos}"; +"lng_send_files_selected" = "{count:_not_used_|# file|# files} selected"; +"lng_send_files" = "Send {count:_not_used_|# file|# files}"; "lng_forward_choose" = "Choose recipient..."; "lng_forward_cant" = "Sorry, no way to forward here :("; diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 15f81695f7..4fea641f13 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,9 - PRODUCTVERSION 0,10,19,9 + FILEVERSION 0,10,19,10 + PRODUCTVERSION 0,10,19,10 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.9" + VALUE "FileVersion", "0.10.19.10" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.9" + VALUE "ProductVersion", "0.10.19.10" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index bff3862dbc..d38f081b9b 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,9 - PRODUCTVERSION 0,10,19,9 + FILEVERSION 0,10,19,10 + PRODUCTVERSION 0,10,19,10 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.9" + VALUE "FileVersion", "0.10.19.10" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.9" + VALUE "ProductVersion", "0.10.19.10" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index dcf84deeb4..c66b03defa 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2533,20 +2533,15 @@ namespace { exif_data_free(exifData); } #endif // OS_MAC_OLD - } else if (opaque && result.hasAlphaChannel()) { - QImage solid(result.width(), result.height(), QImage::Format_ARGB32_Premultiplied); - solid.fill(st::imageBgTransparent->c); - { - QPainter(&solid).drawImage(0, 0, result); - } - result = solid; + } else if (opaque) { + result = Images::prepareOpaque(std_::move(result)); } return result; } QImage readImage(const QString &file, QByteArray *format, bool opaque, bool *animated, QByteArray *content) { QFile f(file); - if (f.size() > MediaViewImageSizeLimit || !f.open(QIODevice::ReadOnly)) { + if (f.size() > kImageSizeLimit || !f.open(QIODevice::ReadOnly)) { if (animated) *animated = false; return QImage(); } @@ -2557,7 +2552,7 @@ namespace { } QPixmap pixmapFromImageInPlace(QImage &&image) { - return QPixmap::fromImage(std_::forward(image), Qt::ColorOnly); + return QPixmap::fromImage(std_::move(image), Qt::ColorOnly); } void regPhotoItem(PhotoData *data, HistoryItem *item) { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 3d433c076d..bb8bb0faca 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -238,8 +238,10 @@ namespace App { void setLaunchState(LaunchState state); void restart(); - QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0); - QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0); + constexpr auto kFileSizeLimit = 1500 * 1024 * 1024; // Load files up to 1500mb + constexpr auto kImageSizeLimit = 64 * 1024 * 1024; // Open images up to 64mb jpg/png/gif + QImage readImage(QByteArray data, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr); + QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0); QPixmap pixmapFromImageInPlace(QImage &&image); void regPhotoItem(PhotoData *data, HistoryItem *item); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 9e8ba6ebda..136c195463 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -1062,7 +1062,7 @@ void AppClass::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId) { int32 filesize = 0; QByteArray data; - ReadyLocalMedia ready(PreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0); + SendMediaReady ready(SendMediaType::Photo, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, 0); connect(App::uploader(), SIGNAL(photoReady(const FullMsgId&,bool,const MTPInputFile&)), App::app(), SLOT(photoUpdated(const FullMsgId&,bool,const MTPInputFile&)), Qt::UniqueConnection); diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index b216b6ece2..f085cd0ae3 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "styles/style_boxes.h" +#include "platform/platform_file_dialog.h" AboutBox::AboutBox() : AbstractBox(st::aboutWidth, qsl("Telegram Desktop")) , _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink) @@ -44,8 +45,6 @@ AboutBox::AboutBox() : AbstractBox(st::aboutWidth, qsl("Telegram Desktop")) connect(_version, SIGNAL(clicked()), this, SLOT(onVersion())); connect(_done, SIGNAL(clicked()), this, SLOT(onClose())); - prepare(); - setAcceptDrops(true); } @@ -90,7 +89,7 @@ void AboutBox::keyPressEvent(QKeyEvent *e) { QString _getCrashReportFile(const QMimeData *m) { if (!m || m->urls().size() != 1 || !m->urls().at(0).isLocalFile()) return QString(); - auto file = psConvertFileUrl(m->urls().at(0)); + auto file = Platform::FileDialog::UrlToLocal(m->urls().at(0)); return file.endsWith(qstr(".telegramcrash"), Qt::CaseInsensitive) ? file : QString(); } diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index b92660e9b1..31fe4264d7 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -45,10 +45,6 @@ void AbstractBox::setAdditionalTitle(const QString &additionalTitle) { update(); } -void AbstractBox::prepare() { - raiseShadow(); -} - void AbstractBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { onClose(); diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index 73c01882c8..cdf5f8deab 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -39,7 +39,6 @@ public: void setTitleText(const QString &title); void setAdditionalTitle(const QString &additionalTitle); void setBlockTitle(bool block, bool withClose = true, bool withShadow = true); - void raiseShadow(); public slots: void onClose(); @@ -49,7 +48,7 @@ protected: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; - void prepare(); + void raiseShadow(); int titleHeight() const; void paintTitle(Painter &p, const QString &title, const QString &additional = QString()); void setMaxHeight(int32 maxHeight); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 5904a4ef47..cb9a570c78 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -87,8 +87,6 @@ void AddContactBox::initBox() { connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); connect(_phone, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - - prepare(); } void AddContactBox::doSetInnerFocus() { @@ -279,13 +277,11 @@ GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : Ab _photo->setClickedCallback([this] { auto imgExtensions = cImgExtensions(); auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter(); - _setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter); + _setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_image), filter); }); subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) { notifyFileQueryUpdated(update); }); - - prepare(); } void GroupInfoBox::doSetInnerFocus() { @@ -459,8 +455,6 @@ SetupChannelBox::SetupChannelBox(ChannelData *channel, bool existing) : Abstract connect(_public, SIGNAL(changed()), this, SLOT(onPrivacyChange())); connect(_private, SIGNAL(changed()), this, SLOT(onPrivacyChange())); - - prepare(); } void SetupChannelBox::doSetInnerFocus() { @@ -673,12 +667,11 @@ void SetupChannelBox::onPrivacyChange() { if (_public->checked()) { if (_tooMuchUsernames) { _private->setChecked(true); - Ui::showLayer(new RevokePublicLinkBox([this, weak_this = weakThis()]() { - if (!weak_this) return; + Ui::showLayer(new RevokePublicLinkBox(base::lambda_guarded(this, [this] { _tooMuchUsernames = false; _public->setChecked(true); onCheck(); - }), KeepOtherLayers); + })), KeepOtherLayers); return; } _link->show(); @@ -825,8 +818,6 @@ _invertOrder(!peer->isChat() && langFirstNameGoesSecond()) { connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); _last->setVisible(!_peer->isChat()); - - prepare(); } void EditNameTitleBox::doSetInnerFocus() { @@ -983,8 +974,6 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox() connect(_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink())); _publicLink->setVisible(_channel->canEditUsername()); _sign->setVisible(!_channel->isMegagroup()); - - prepare(); } void EditChannelBox::doSetInnerFocus() { @@ -1163,8 +1152,6 @@ RevokePublicLinkBox::RevokePublicLinkBox(base::lambda &&revokeCallback) connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); subscribe(FileDownload::ImageLoaded(), [this] { update(); }); - - prepare(); } void RevokePublicLinkBox::updateMaxHeight() { @@ -1215,11 +1202,10 @@ void RevokePublicLinkBox::mouseReleaseEvent(QMouseEvent *e) { QPointer weakThis; PeerData *pressed; }; - weakRevokeConfirmBox->setConfirmedCallback([this, data = std_::make_unique(weakThis(), pressed)]() { - if (!data->weakThis) return; + weakRevokeConfirmBox->setConfirmedCallback(base::lambda_guarded(this, [this, pressed]() { if (_revokeRequestId) return; - _revokeRequestId = MTP::send(MTPchannels_UpdateUsername(data->pressed->asChannel()->inputChannel, MTP_string("")), rpcDone(&RevokePublicLinkBox::revokeLinkDone), rpcFail(&RevokePublicLinkBox::revokeLinkFail)); - }); + _revokeRequestId = MTP::send(MTPchannels_UpdateUsername(pressed->asChannel()->inputChannel, MTP_string("")), rpcDone(&RevokePublicLinkBox::revokeLinkDone), rpcFail(&RevokePublicLinkBox::revokeLinkFail)); + })); Ui::showLayer(weakRevokeConfirmBox, KeepOtherLayers); } } diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp index 0b4bc5b40b..1da05fa02a 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.cpp +++ b/Telegram/SourceFiles/boxes/autolockbox.cpp @@ -52,7 +52,6 @@ AutoLockBox::AutoLockBox() : _close(this, lang(lng_box_ok), st::defaultBoxButton connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); - prepare(); } void AutoLockBox::onChange() { diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 01ae2ca117..3afed1095d 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -36,7 +36,7 @@ BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) connect(_inner, SIGNAL(backgroundChosen(int)), this, SLOT(onBackgroundChosen(int))); - prepare(); + raiseShadow(); } void BackgroundBox::onBackgroundChosen(int index) { diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 02cfd56f4b..f668a81285 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -142,8 +142,11 @@ boxTextStyle: TextStyle(defaultTextStyle) { lineHeight: 22px; } -boxPhotoPadding: margins(28px, 28px, 28px, 18px); -boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); +boxPhotoTitleFont: font(16px semibold); +boxPhotoTitlePosition: point(28px, 26px); +boxPhotoPadding: margins(28px, 28px, 28px, 0px); +boxPhotoCompressedSkip: 20px; +boxPhotoCaptionSkip: 22px; boxPhotoTextFg: #808080; cropPointSize: 10px; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 96ab53c80d..a8cd84a9a6 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -66,8 +66,6 @@ void ConfirmBox::init(const QString &text) { connect(this, SIGNAL(confirmed()), this, SLOT(onCancel())); } onTextUpdated(); - - prepare(); } void ConfirmBox::onConfirmPressed() { @@ -217,8 +215,6 @@ MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth) setMaxHeight(st::boxPadding.top() + _textHeight + st::boxTextFont->height + st::boxTextFont->height * 2 + st::newGroupLinkPadding.bottom() + st::boxButtonPadding.top() + _close->height() + st::boxButtonPadding.bottom()); connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); - - prepare(); } void MaxInviteBox::mouseMoveEvent(QMouseEvent *e) { @@ -312,8 +308,6 @@ ConvertToSupergroupBox::ConvertToSupergroupBox(ChatData *chat) : AbstractBox(st: connect(_convert, SIGNAL(clicked()), this, SLOT(onConvert())); connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - - prepare(); } void ConvertToSupergroupBox::onConvert() { diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.cpp b/Telegram/SourceFiles/boxes/confirmphonebox.cpp index 3d6fd2c301..1a636c4e07 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.cpp +++ b/Telegram/SourceFiles/boxes/confirmphonebox.cpp @@ -124,7 +124,7 @@ void ConfirmPhoneBox::launch() { connect(&_callTimer, SIGNAL(timeout()), this, SLOT(onCallStatusTimer())); showChildren(); - prepare(); + raiseShadow(); Ui::showLayer(this); } diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index 5b45cdf994..ab031abbca 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -56,8 +56,6 @@ ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth, lang(lng_connection_h connect(_passwordInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); updateControlsVisibility(); - - prepare(); } void ConnectionBox::updateControlsVisibility() { @@ -218,8 +216,6 @@ AutoDownloadBox::AutoDownloadBox() : AbstractBox(st::boxWidth) connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - - prepare(); } void AutoDownloadBox::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 6ae34786c5..7f8d8b165a 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -172,7 +172,7 @@ void ContactsBox::init() { _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); - prepare(); + raiseShadow(); } bool ContactsBox::onSearchByUsername(bool searchCache) { diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp index 1e9ef5ba38..3534630730 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp +++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp @@ -52,8 +52,6 @@ DownloadPathBox::DownloadPathBox() : AbstractBox() setPathText(QDir::toNativeSeparators(_path)); } updateControlsVisibility(); - - prepare(); } void DownloadPathBox::updateControlsVisibility() { diff --git a/Telegram/SourceFiles/boxes/emojibox.cpp b/Telegram/SourceFiles/boxes/emojibox.cpp index ab571a76e5..46a342a6f3 100644 --- a/Telegram/SourceFiles/boxes/emojibox.cpp +++ b/Telegram/SourceFiles/boxes/emojibox.cpp @@ -81,7 +81,7 @@ EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]) { resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + 2 * st::emojiReplacePadding, titleHeight() + st::emojiReplacePadding + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + st::emojiReplacePadding); - prepare(); + raiseShadow(); } void EmojiBox::fillBlocks() { diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index 070cfdc4c9..862f7d4763 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -64,7 +64,6 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); - prepare(); } void LanguageBox::mousePressEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/boxes/localstoragebox.cpp b/Telegram/SourceFiles/boxes/localstoragebox.cpp index c9572a1938..bb08d17aab 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.cpp +++ b/Telegram/SourceFiles/boxes/localstoragebox.cpp @@ -43,7 +43,6 @@ LocalStorageBox::LocalStorageBox() : AbstractBox() updateControls(); checkLocalStoredCounts(); - prepare(); } void LocalStorageBox::updateControls() { diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index a91088baf9..1fac7d7b72 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -75,7 +75,7 @@ MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox connect(&_loadTimer, SIGNAL(timeout()), _inner, SLOT(load())); - prepare(); + raiseShadow(); } void MembersBox::keyPressEvent(QKeyEvent *e) { diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index a665c40c1c..87a73d079f 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -127,8 +127,6 @@ NotificationsBox::NotificationsBox() : AbstractBox() prepareNotificationSampleSmall(); prepareNotificationSampleLarge(); setMaxHeight(st::notificationsBoxHeight); - - prepare(); } void NotificationsBox::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index b03a493816..a47d574807 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -114,7 +114,7 @@ void PasscodeBox::init() { _passwordHint->setVisible(!_turningOff && _cloudPwd); _recoverEmail->setVisible(!_turningOff && _cloudPwd && _curSalt.isEmpty()); - prepare(); + raiseShadow(); } void PasscodeBox::onSubmit() { @@ -463,7 +463,7 @@ RecoverBox::RecoverBox(const QString &pattern) : AbstractBox(st::boxWidth, lang( connect(_recoverCode, SIGNAL(changed()), this, SLOT(onCodeChanged())); connect(_recoverCode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - prepare(); + raiseShadow(); } void RecoverBox::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index b632468693..07890d0831 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -49,8 +49,6 @@ ReportBox::ReportBox(ChannelData *channel) : AbstractBox(st::boxWidth) connect(_reasonOther, SIGNAL(changed()), this, SLOT(onChange())); updateMaxHeight(); - - prepare(); } void ReportBox::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp similarity index 56% rename from Telegram/SourceFiles/boxes/photosendbox.cpp rename to Telegram/SourceFiles/boxes/send_files_box.cpp index c8de5ff9ca..7abdbb0853 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -19,199 +19,201 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "boxes/photosendbox.h" +#include "boxes/send_files_box.h" #include "lang.h" #include "localstorage.h" #include "mainwidget.h" -#include "photosendbox.h" #include "history/history_media_types.h" +#include "ui/filedialog.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "styles/style_history.h" #include "styles/style_boxes.h" -PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth) -, _file(file) -, _animated(false) +namespace { + +constexpr auto kMinPreviewWidth = 20; + +} // namespace + +SendFilesBox::SendFilesBox(const QString &filepath, QImage image, CompressConfirm compressed, bool animated) : AbstractBox(st::boxWideWidth) +, _files(filepath) +, _image(image) +, _compressConfirm(compressed) +, _animated(image.isNull() ? false : animated) , _caption(this, st::confirmCaptionArea, lang(lng_photo_caption)) -, _compressedFromSettings(_file->type == PrepareAuto) -, _compressed(this, lang(lng_send_image_compressed), _compressedFromSettings ? cCompressPastedImage() : true, st::defaultBoxCheckbox) , _send(this, lang(lng_send_button), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _thumbx(0) -, _thumby(0) -, _thumbw(0) -, _thumbh(0) -, _statusw(0) -, _isImage(false) -, _replyTo(_file->to.replyTo) -, _confirmed(false) { - connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); - connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - - _animated = false; - QSize dimensions; - if (_file->photo.type() != mtpc_photoEmpty) { - _file->type = PreparePhoto; - } else if (_file->document.type() == mtpc_document) { - const auto &document(_file->document.c_document()); - const auto &attributes(document.vattributes.c_vector().v); - for (int32 i = 0, l = attributes.size(); i < l; ++i) { - if (attributes.at(i).type() == mtpc_documentAttributeAnimated) { - _animated = true; - } else if (attributes.at(i).type() == mtpc_documentAttributeImageSize) { - dimensions = QSize(attributes.at(i).c_documentAttributeImageSize().vw.v, attributes.at(i).c_documentAttributeImageSize().vh.v); - } else if (attributes.at(i).type() == mtpc_documentAttributeVideo) { - dimensions = QSize(attributes.at(i).c_documentAttributeVideo().vw.v, attributes.at(i).c_documentAttributeVideo().vh.v); +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { + if (!image.isNull()) { + if (!_animated && _compressConfirm == CompressConfirm::None) { + auto originalWidth = image.width(); + auto originalHeight = image.height(); + auto thumbWidth = st::msgFileThumbSize; + if (originalWidth > originalHeight) { + thumbWidth = (originalWidth * st::msgFileThumbSize) / originalHeight; } - } - if (dimensions.isEmpty()) _animated = false; - } - if (_file->type == PreparePhoto || _animated) { - int32 maxW = 0, maxH = 0; - if (_animated) { - int32 limitW = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); - int32 limitH = st::confirmMaxHeight; - maxW = qMax(dimensions.width(), 1); - maxH = qMax(dimensions.height(), 1); - if (maxW * limitH > maxH * limitW) { - if (maxW < limitW) { - maxH = maxH * limitW / maxW; - maxW = limitW; - } - } else { - if (maxH < limitH) { - maxW = maxW * limitH / maxH; - maxH = limitH; - } - } - _thumb = imagePix(_file->thumb.toImage(), maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), ImagePixOption::Smooth | ImagePixOption::Blurred, maxW, maxH); + auto options = Images::Option::Smooth | Images::Option::RoundedSmall | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::RoundedBottomLeft | Images::Option::RoundedBottomRight; + _fileThumb = Images::pixmap(image, thumbWidth * cIntRetinaFactor(), 0, options, st::msgFileThumbSize, st::msgFileThumbSize); } else { - for (PreparedPhotoThumbs::const_iterator i = _file->photoThumbs.cbegin(), e = _file->photoThumbs.cend(); i != e; ++i) { - if (i->width() >= maxW && i->height() >= maxH) { - _thumb = *i; - maxW = _thumb.width(); - maxH = _thumb.height(); + auto maxW = 0; + auto maxH = 0; + if (_animated) { + auto limitW = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + auto limitH = st::confirmMaxHeight; + maxW = qMax(image.width(), 1); + maxH = qMax(image.height(), 1); + if (maxW * limitH > maxH * limitW) { + if (maxW < limitW) { + maxH = maxH * limitW / maxW; + maxW = limitW; + } + } else { + if (maxH < limitH) { + maxW = maxW * limitH / maxH; + maxH = limitH; + } } + image = Images::prepare(image, maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), Images::Option::Smooth | Images::Option::Blurred, maxW, maxH); } - } - int32 tw = _thumb.width(), th = _thumb.height(); - if (!tw || !th) { - tw = th = 1; - } - _thumbw = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); - if (_thumb.width() < _thumbw) { - _thumbw = (_thumb.width() > 20) ? _thumb.width() : 20; - } - int32 maxthumbh = qMin(qRound(1.5 * _thumbw), int(st::confirmMaxHeight)); - _thumbh = qRound(th * float64(_thumbw) / tw); - if (_thumbh > maxthumbh) { - _thumbw = qRound(_thumbw * float64(maxthumbh) / _thumbh); - _thumbh = maxthumbh; - if (_thumbw < 10) { - _thumbw = 10; + auto originalWidth = image.width(); + auto originalHeight = image.height(); + if (!originalWidth || !originalHeight) { + originalWidth = originalHeight = 1; } - } - _thumbx = (width() - _thumbw) / 2; + _previewWidth = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + if (image.width() < _previewWidth) { + _previewWidth = qMax(image.width(), kMinPreviewWidth); + } + auto maxthumbh = qMin(qRound(1.5 * _previewWidth), st::confirmMaxHeight); + _previewHeight = qRound(originalHeight * float64(_previewWidth) / originalWidth); + if (_previewHeight > maxthumbh) { + _previewWidth = qRound(_previewWidth * float64(maxthumbh) / _previewHeight); + accumulate_max(_previewWidth, kMinPreviewWidth); + _previewHeight = maxthumbh; + } + _previewLeft = (width() - _previewWidth) / 2; - _thumb = App::pixmapFromImageInPlace(_thumb.toImage().scaled(_thumbw * cIntRetinaFactor(), _thumbh * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - _thumb.setDevicePixelRatio(cRetinaFactor()); - } else { - if (_file->thumb.isNull()) { - _thumbw = 0; + image = std_::move(image).scaled(_previewWidth * cIntRetinaFactor(), _previewHeight * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + image = Images::prepareOpaque(std_::move(image)); + _preview = App::pixmapFromImageInPlace(std_::move(image)); + _preview.setDevicePixelRatio(cRetinaFactor()); + } + } + if (_preview.isNull()) { + if (filepath.isEmpty()) { + auto filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); + _nameText.setText(st::semiboldFont, filename, _textNameOptions); + _statusText = qsl("%1x%2").arg(_image.width()).arg(_image.height()); + _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + _fileIsImage = true; } else { - _thumb = _file->thumb; - int32 tw = _thumb.width(), th = _thumb.height(); - if (tw > th) { - _thumbw = (tw * st::msgFileThumbSize) / th; - } else { - _thumbw = st::msgFileThumbSize; - } - auto options = ImagePixOption::Smooth | ImagePixOption::RoundedSmall | ImagePixOption::RoundedTopLeft | ImagePixOption::RoundedTopRight | ImagePixOption::RoundedBottomLeft | ImagePixOption::RoundedBottomRight; - _thumb = imagePix(_thumb.toImage(), _thumbw * cIntRetinaFactor(), 0, options, st::msgFileThumbSize, st::msgFileThumbSize); + auto fileinfo = QFileInfo(filepath); + auto filename = fileinfo.fileName(); + _nameText.setText(st::semiboldFont, filename, _textNameOptions); + _statusText = formatSizeText(fileinfo.size()); + _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + _fileIsImage = fileIsImage(filename, mimeTypeForFile(fileinfo).name()); } - - _name.setText(st::semiboldFont, _file->filename, _textNameOptions); - _status = formatSizeText(_file->filesize); - _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status)); - _isImage = fileIsImage(_file->filename, _file->filemime); } - if (_file->type != PreparePhoto) { - _compressed->hide(); - } - - updateBoxSize(); - _caption->setMaxLength(MaxPhotoCaption); - _caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmitBoth); - connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); - connect(_caption, SIGNAL(resized()), this, SLOT(onCaptionResized())); - connect(_caption, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); - connect(_caption, SIGNAL(cancelled()), this, SLOT(onClose())); - - prepare(); + setup(); } -PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo) : AbstractBox(st::boxWideWidth) +SendFilesBox::SendFilesBox(const QStringList &files, CompressConfirm compressed) : AbstractBox(st::boxWideWidth) +, _files(files) +, _compressConfirm(compressed) , _caption(this, st::confirmCaptionArea, lang(lng_photo_caption)) -, _compressed(this, lang(lng_send_image_compressed), true) , _send(this, lang(lng_send_button), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _thumbx(0) -, _thumby(0) -, _thumbw(0) -, _thumbh(0) -, _statusw(0) -, _isImage(false) -, _phone(phone) -, _fname(fname) -, _lname(lname) -, _replyTo(replyTo) -, _confirmed(false) { - connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); - connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { + updateTitleText(); - _compressed->hide(); - _caption->hide(); - - _name.setText(st::semiboldFont, lng_full_name(lt_first_name, _fname, lt_last_name, _lname), _textNameOptions); - _status = _phone; - _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status)); - - updateBoxSize(); - prepare(); + setup(); } -void PhotoSendBox::onCompressedChange() { - if (_caption->isHidden()) { - setFocus(); - } else { - _caption->setFocus(); +SendFilesBox::SendFilesBox(const QString &phone, const QString &firstname, const QString &lastname) : AbstractBox(st::boxWideWidth) +, _contactPhone(phone) +, _contactFirstName(firstname) +, _contactLastName(lastname) +, _send(this, lang(lng_send_button), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { + _nameText.setText(st::semiboldFont, lng_full_name(lt_first_name, _contactFirstName, lt_last_name, _contactLastName), _textNameOptions); + _statusText = _contactPhone; + _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + + setup(); +} + +void SendFilesBox::setup() { + _send->setClickedCallback([this] { onSend(); }); + _cancel->setClickedCallback([this] { onClose(); }); + + if (_compressConfirm != CompressConfirm::None) { + auto compressed = (_compressConfirm == CompressConfirm::Auto) ? cCompressPastedImage() : (_compressConfirm == CompressConfirm::Yes); + auto text = lng_send_images_compress(lt_count, _files.size()); + _compressed.create(this, text, compressed, st::defaultBoxCheckbox); + connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); } + if (_caption) { + _caption->setMaxLength(MaxPhotoCaption); + _caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmitBoth); + connect(_caption, SIGNAL(resized()), this, SLOT(onCaptionResized())); + connect(_caption, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); + connect(_caption, SIGNAL(cancelled()), this, SLOT(onClose())); + } + _send->setText(getSendButtonText()); updateBoxSize(); - resizeEvent(0); +} + +QString SendFilesBox::getSendButtonText() const { + if (!_contactPhone.isEmpty()) { + return lang(lng_send_button); + } else if (_compressed && _compressed->checked()) { + return lng_send_photos(lt_count, _files.size()); + } + return lng_send_files(lt_count, _files.size()); +} + +void SendFilesBox::onCompressedChange() { + doSetInnerFocus(); + _send->setText(getSendButtonText()); + updateControlsGeometry(); +} + +void SendFilesBox::onCaptionResized() { + updateBoxSize(); + updateControlsGeometry(); update(); } -void PhotoSendBox::onCaptionResized() { - updateBoxSize(); - resizeEvent(0); +void SendFilesBox::updateTitleText() { + _titleText = (_compressConfirm == CompressConfirm::None) ? lng_send_files_selected(lt_count, _files.size()) : lng_send_images_selected(lt_count, _files.size()); update(); } -void PhotoSendBox::updateBoxSize() { - if (_file && (_file->type == PreparePhoto || _animated)) { - setMaxHeight(st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + (_animated ? 0 : (st::boxPhotoCompressedPadding.top() + _compressed->height())) + st::boxPhotoCompressedPadding.bottom() + _caption->height() + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom()); - } else if (_thumbw) { - setMaxHeight(st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption->height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom()); +void SendFilesBox::updateBoxSize() { + auto newHeight = 0; + if (!_preview.isNull()) { + newHeight += st::boxPhotoPadding.top() + _previewHeight; + } else if (!_fileThumb.isNull()) { + newHeight += st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + } else if (_files.size() > 1) { + newHeight += titleHeight(); } else { - setMaxHeight(st::boxPhotoPadding.top() + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption->height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom()); + newHeight += st::boxPhotoPadding.top() + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } + if (_compressed) { + newHeight += st::boxPhotoCompressedSkip + _compressed->height(); + } + if (_caption) { + newHeight += st::boxPhotoCaptionSkip + _caption->height(); + } + newHeight += st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom(); + setMaxHeight(newHeight); } -void PhotoSendBox::keyPressEvent(QKeyEvent *e) { +void SendFilesBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { onSend((e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier)) && e->modifiers().testFlag(Qt::ShiftModifier)); } else { @@ -219,21 +221,27 @@ void PhotoSendBox::keyPressEvent(QKeyEvent *e) { } } -void PhotoSendBox::paintEvent(QPaintEvent *e) { +void SendFilesBox::paintEvent(QPaintEvent *e) { AbstractBox::paintEvent(e); Painter p(this); - if (_file && (_file->type == PreparePhoto || _animated)) { - if (_thumbx > st::boxPhotoPadding.left()) { - p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _thumbx - st::boxPhotoPadding.left(), _thumbh, st::confirmBg->b); + if (!_titleText.isEmpty()) { + p.setFont(st::boxPhotoTitleFont); + p.setPen(st::boxTitleFg); + p.drawTextLeft(st::boxPhotoTitlePosition.x(), st::boxPhotoTitlePosition.y(), width(), _titleText); + } + + if (!_preview.isNull()) { + if (_previewLeft > st::boxPhotoPadding.left()) { + p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _previewLeft - st::boxPhotoPadding.left(), _previewHeight, st::confirmBg); } - if (_thumbx + _thumbw < width() - st::boxPhotoPadding.right()) { - p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, _thumbh, st::confirmBg->b); + if (_previewLeft + _previewWidth < width() - st::boxPhotoPadding.right()) { + p.fillRect(_previewLeft + _previewWidth, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _previewLeft - _previewWidth, _previewHeight, st::confirmBg); } - p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), _thumb); + p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), _preview); if (_animated) { - QRect inner(_thumbx + (_thumbw - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_thumbh - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + auto inner = QRect(_previewLeft + (_previewWidth - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_previewHeight - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); p.setBrush(st::msgDateImgBg); @@ -244,116 +252,105 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { auto icon = &st::historyFileInPlay; icon->paintInCenter(p, inner); } - } else { - int32 w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); - int32 h = _thumbw ? (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()) : (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()); - int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; - if (_thumbw) { + } else if (_files.size() < 2) { + auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + auto h = _fileThumb.isNull() ? (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()) : (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()); + auto nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + if (_fileThumb.isNull()) { + nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + nametop = st::msgFileNameTop; + nameright = st::msgFilePadding.left(); + statustop = st::msgFileStatusTop; + } else { nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); nametop = st::msgFileThumbNameTop; nameright = st::msgFileThumbPadding.left(); statustop = st::msgFileThumbStatusTop; linktop = st::msgFileThumbLinkTop; - } else { - nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); - nametop = st::msgFileNameTop; - nameright = st::msgFilePadding.left(); - statustop = st::msgFileStatusTop; - } - int32 namewidth = w - nameleft - (_thumbw ? st::msgFileThumbPadding.left() : st::msgFilePadding.left()); - if (namewidth > _statusw) { - w -= (namewidth - _statusw); - namewidth = _statusw; } + auto namewidth = w - nameleft - (_fileThumb.isNull() ? st::msgFilePadding.left() : st::msgFileThumbPadding.left()); int32 x = (width() - w) / 2, y = st::boxPhotoPadding.top(); App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow); - if (_thumbw) { - QRect rthumb(rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width())); - p.drawPixmap(rthumb.topLeft(), _thumb); - } else if (_file) { - QRect inner(rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width())); - p.setPen(Qt::NoPen); - p.setBrush(st::msgFileOutBg); + if (_fileThumb.isNull()) { + if (_contactPhone.isNull()) { + QRect inner(rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width())); + p.setPen(Qt::NoPen); + p.setBrush(st::msgFileOutBg); - p.setRenderHint(QPainter::HighQualityAntialiasing); - p.drawEllipse(inner); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); - auto icon = &(_isImage ? st::historyFileOutImage : st::historyFileOutDocument); - icon->paintInCenter(p, inner); + auto &icon = _fileIsImage ? st::historyFileOutImage : st::historyFileOutDocument; + icon.paintInCenter(p, inner); + } else { + p.drawPixmapLeft(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), userDefPhoto(1)->pixCircled(st::msgFileSize)); + } } else { - p.drawPixmapLeft(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), userDefPhoto(1)->pixCircled(st::msgFileSize)); + QRect rthumb(rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width())); + p.drawPixmap(rthumb.topLeft(), _fileThumb); } p.setFont(st::semiboldFont); p.setPen(st::historyFileNameOutFg); - _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); + _nameText.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); auto &status = st::mediaOutFg; p.setFont(st::normalFont); p.setPen(status); - p.drawTextLeft(x + nameleft, y + statustop, width(), _status); + p.drawTextLeft(x + nameleft, y + statustop, width(), _statusText); } } -void PhotoSendBox::resizeEvent(QResizeEvent *e) { - _send->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _send->height()); - _cancel->moveToRight(st::boxButtonPadding.right() + _send->width() + st::boxButtonPadding.left(), _send->y()); - _caption->resize(st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), _caption->height()); - _caption->moveToLeft(st::boxPhotoPadding.left(), _send->y() - st::boxButtonPadding.top() - _caption->height()); - _compressed->moveToLeft(st::boxPhotoPadding.left(), st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.top()); +void SendFilesBox::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); AbstractBox::resizeEvent(e); } -void PhotoSendBox::closePressed() { - if (!_confirmed && App::main()) { - if (_file) { - App::main()->onSendFileCancel(_file); - } else { - App::main()->onShareContactCancel(); - } +void SendFilesBox::updateControlsGeometry() { + _send->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _send->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _send->width() + st::boxButtonPadding.left(), _send->y()); + auto bottom = _send->y() - st::boxButtonPadding.top(); + if (_caption) { + _caption->resize(st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), _caption->height()); + _caption->moveToLeft(st::boxPhotoPadding.left(), bottom - _caption->height()); + bottom -= st::boxPhotoCaptionSkip + _caption->height(); + } + if (_compressed) { + _compressed->moveToLeft(st::boxPhotoPadding.left(), bottom - _compressed->height()); + bottom -= st::boxPhotoCompressedSkip + _compressed->height(); } } -void PhotoSendBox::doSetInnerFocus() { - if (_caption->isHidden()) { +void SendFilesBox::doSetInnerFocus() { + if (!_caption || _caption->isHidden()) { setFocus(); } else { _caption->setFocus(); } } -void PhotoSendBox::onSend(bool ctrlShiftEnter) { - if (App::main()) { - if (_file) { - if (_compressed->isHidden()) { - if (_file->type == PrepareAuto) { - _file->type = PrepareDocument; - } - } else { - if (_compressedFromSettings && _compressed->checked() != cCompressPastedImage()) { - cSetCompressPastedImage(_compressed->checked()); - Local::writeUserSettings(); - } - if (_compressed->checked()) { - _file->type = PreparePhoto; - } else { - _file->type = PrepareDocument; - } - } - if (!_caption->isHidden()) { - _file->caption = prepareText(_caption->getLastText(), true); - } - App::main()->onSendFileConfirm(_file, ctrlShiftEnter); - } else { - App::main()->onShareContactConfirm(_phone, _fname, _lname, _replyTo, ctrlShiftEnter); - } +void SendFilesBox::onSend(bool ctrlShiftEnter) { + if (_compressed && _compressConfirm == CompressConfirm::Auto && _compressed->checked() != cCompressPastedImage()) { + cSetCompressPastedImage(_compressed->checked()); + Local::writeUserSettings(); } _confirmed = true; + if (_confirmedCallback) { + auto compressed = _compressed ? _compressed->checked() : false; + auto caption = _caption ? prepareText(_caption->getLastText(), true) : QString(); + _confirmedCallback(_files, compressed, caption, ctrlShiftEnter); + } onClose(); } +void SendFilesBox::closePressed() { + if (!_confirmed && _cancelledCallback) { + _cancelledCallback(); + } +} + EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) , _msgId(msg->fullId()) , _animated(false) @@ -421,8 +418,8 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) } else { _thumbw = st::msgFileThumbSize; } - auto options = ImagePixOption::Smooth | ImagePixOption::RoundedSmall | ImagePixOption::RoundedTopLeft | ImagePixOption::RoundedTopRight | ImagePixOption::RoundedBottomLeft | ImagePixOption::RoundedBottomRight; - _thumb = imagePix(image->pix().toImage(), _thumbw * cIntRetinaFactor(), 0, options, st::msgFileThumbSize, st::msgFileThumbSize); + auto options = Images::Option::Smooth | Images::Option::RoundedSmall | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::RoundedBottomLeft | Images::Option::RoundedBottomRight; + _thumb = Images::pixmap(image->pix().toImage(), _thumbw * cIntRetinaFactor(), 0, options, st::msgFileThumbSize, st::msgFileThumbSize); } if (doc) { @@ -453,11 +450,11 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) maxH = limitH; } } - _thumb = image->pixNoCache(maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), ImagePixOption::Smooth | ImagePixOption::Blurred, maxW, maxH); + _thumb = image->pixNoCache(maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), Images::Option::Smooth | Images::Option::Blurred, maxW, maxH); } else { maxW = dimensions.width(); maxH = dimensions.height(); - _thumb = image->pixNoCache(maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), ImagePixOption::Smooth, maxW, maxH); + _thumb = image->pixNoCache(maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), Images::Option::Smooth, maxW, maxH); } int32 tw = _thumb.width(), th = _thumb.height(); if (!tw || !th) { @@ -501,8 +498,6 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) QTextCursor c(_field->textCursor()); c.movePosition(QTextCursor::End); _field->setTextCursor(c); - - prepare(); } bool EditCaptionBox::captionFound() const { @@ -516,7 +511,7 @@ void EditCaptionBox::onCaptionResized() { } void EditCaptionBox::updateBoxSize() { - int32 bottomh = st::boxPhotoCompressedPadding.bottom() + _field->height() + st::normalFont->height + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + auto bottomh = st::boxPhotoCaptionSkip + _field->height() + st::normalFont->height + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); if (_photo || _animated) { setMaxHeight(st::boxPhotoPadding.top() + _thumbh + bottomh); } else if (_thumbw) { diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/send_files_box.h similarity index 62% rename from Telegram/SourceFiles/boxes/photosendbox.h rename to Telegram/SourceFiles/boxes/send_files_box.h index c51641021c..46d11bc72c 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -29,17 +29,25 @@ class RoundButton; class InputArea; } // namespace Ui -class PhotoSendBox : public AbstractBox { +class SendFilesBox : public AbstractBox { Q_OBJECT public: - PhotoSendBox(const FileLoadResultPtr &file); - PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo); + SendFilesBox(const QString &filepath, QImage image, CompressConfirm compressed, bool animated = false); + SendFilesBox(const QStringList &files, CompressConfirm compressed); + SendFilesBox(const QString &phone, const QString &firstname, const QString &lastname); + + void setConfirmedCallback(base::lambda &&callback) { + _confirmedCallback = std_::move(callback); + } + void setCancelledCallback(base::lambda &&callback) { + _cancelledCallback = std_::move(callback); + } public slots: void onCompressedChange(); - void onCaptionResized(); void onSend(bool ctrlShiftEnter = false); + void onCaptionResized(); protected: void keyPressEvent(QKeyEvent *e) override; @@ -50,32 +58,44 @@ protected: void doSetInnerFocus() override; private: + void setup(); + void updateTitleText(); void updateBoxSize(); + void updateControlsGeometry(); + QString getSendButtonText() const; - FileLoadResultPtr _file; - bool _animated; + QString _titleText; + QStringList _files; + const QImage _image; - QPixmap _thumb; + CompressConfirm _compressConfirm = CompressConfirm::None; + bool _animated = false; - ChildWidget _caption; - bool _compressedFromSettings; - ChildWidget _compressed; + QPixmap _preview; + int _previewLeft = 0; + int _previewWidth = 0; + int _previewHeight = 0; + + QPixmap _fileThumb; + Text _nameText; + bool _fileIsImage = false; + QString _statusText; + int _statusWidth = 0; + + QString _contactPhone; + QString _contactFirstName; + QString _contactLastName; + + base::lambda _confirmedCallback; + base::lambda _cancelledCallback; + bool _confirmed = false; + + ChildWidget _caption = { nullptr }; + ChildWidget _compressed = { nullptr }; ChildWidget _send; ChildWidget _cancel; - int32 _thumbx, _thumby, _thumbw, _thumbh; - Text _name; - QString _status; - int32 _statusw; - bool _isImage; - - QString _phone, _fname, _lname; - - MsgId _replyTo; - - bool _confirmed; - }; class EditCaptionBox : public AbstractBox, public RPCSender { diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 6affcfadb6..5f9491a7dc 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -52,7 +52,7 @@ SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) setLoading(true); - prepare(); + raiseShadow(); MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations)); } diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 675bd5157b..8dbc7d46a5 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -82,7 +82,7 @@ ShareBox::ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback, updateButtonsVisibility(); - prepare(); + raiseShadow(); } int ShareBox::getTopScrollSkip() const { diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index fcf0a85095..8d653fe2f8 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -265,7 +265,7 @@ void StickersBox::setup() { rebuildList(); - prepare(); + raiseShadow(); } void StickersBox::refreshTabs() { diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index da38fa20ae..92dbf50a4f 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -63,7 +63,7 @@ StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st:: onScroll(); - prepare(); + raiseShadow(); } void StickerSetBox::onInstalled(uint64 setId) { diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index 85c1b9db63..c67cbf1238 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -57,7 +57,7 @@ _about(st::boxWidth - st::usernamePadding.left()) { updateLinkText(); - prepare(); + raiseShadow(); } void UsernameBox::doSetInnerFocus() { diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 9ed902b86a..20d2294178 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -120,7 +120,6 @@ enum { AnimationInMemory = 10 * 1024 * 1024, // 10 Mb gif and mp4 animations held in memory while playing - MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app MaxZoomLevel = 7, // x8 ZoomToScreenLevel = 1024, // just constant @@ -351,8 +350,6 @@ enum { DownloadPartSize = 64 * 1024, // 64kb for photo DocumentDownloadPartSize = 128 * 1024, // 128kb for document - MaxUploadPhotoSize = 256 * 1024 * 1024, // 256mb photos max - MaxUploadDocumentSize = 1500 * 1024 * 1024, // 1500mb documents max UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb MaxFileQueries = 16, // max 16 file parts downloaded at the same time MaxWebFileQueries = 8, // max 8 http[s] files downloaded at the same time diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index 9c368655bf..1f56b04f91 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -66,9 +66,3 @@ friend Q_DECL_CONSTEXPR QFlags operator|(Flags::enum_type f1, Q_DECLARE_FRIEND_INCOMPATIBLE_FLAGS(Flags) #endif // OS_MAC_OLD - -// using for_const instead of plain range-based for loop to ensure usage of const_iterator -// it is important for the copy-on-write Qt containers -// if you have "QVector v" then "for (T * const p : v)" will still call QVector::detach(), -// while "for_const (T *p, v)" won't and "for_const (T *&p, v)" won't compile -#define for_const(range_declaration, range_expression) for (range_declaration : std_::as_const(range_expression)) diff --git a/Telegram/SourceFiles/core/lambda.h b/Telegram/SourceFiles/core/lambda.h index dca6cbb58c..c82886f2a6 100644 --- a/Telegram/SourceFiles/core/lambda.h +++ b/Telegram/SourceFiles/core/lambda.h @@ -25,222 +25,222 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace base { namespace internal { -template -struct lambda_wrap_helper_base { - using construct_copy_other_type = void(*)(void *, const void *); // dst, src - using construct_move_other_type = void(*)(void *, void *); // dst, src - using call_type = Return(*)(const void *, Args...); - using destruct_type = void(*)(const void *); + template + struct lambda_wrap_helper_base { + using construct_copy_other_type = void(*)(void *, const void *); // dst, src + using construct_move_other_type = void(*)(void *, void *); // dst, src + using call_type = Return(*)(const void *, Args...); + using destruct_type = void(*)(const void *); - lambda_wrap_helper_base() = delete; - lambda_wrap_helper_base(const lambda_wrap_helper_base &other) = delete; - lambda_wrap_helper_base &operator=(const lambda_wrap_helper_base &other) = delete; + lambda_wrap_helper_base() = delete; + lambda_wrap_helper_base(const lambda_wrap_helper_base &other) = delete; + lambda_wrap_helper_base &operator=(const lambda_wrap_helper_base &other) = delete; - lambda_wrap_helper_base( - construct_copy_other_type construct_copy_other, - construct_move_other_type construct_move_other, - call_type call, - destruct_type destruct) - : construct_copy_other(construct_copy_other) - , construct_move_other(construct_move_other) - , call(call) - , destruct(destruct) { - } + lambda_wrap_helper_base( + construct_copy_other_type construct_copy_other, + construct_move_other_type construct_move_other, + call_type call, + destruct_type destruct) + : construct_copy_other(construct_copy_other) + , construct_move_other(construct_move_other) + , call(call) + , destruct(destruct) { + } - const construct_copy_other_type construct_copy_other; - const construct_move_other_type construct_move_other; - const call_type call; - const destruct_type destruct; + const construct_copy_other_type construct_copy_other; + const construct_move_other_type construct_move_other; + const call_type call; + const destruct_type destruct; - static constexpr size_t kFullStorageSize = 24U + sizeof(void*); - static constexpr size_t kStorageSize = kFullStorageSize - sizeof(void*); - using alignment = uint64; + static constexpr size_t kFullStorageSize = 24U + sizeof(void*); + static constexpr size_t kStorageSize = kFullStorageSize - sizeof(void*); + using alignment = uint64; - template - using IsLarge = std_::integral_constant) <= kStorageSize)>; + template + using IsLarge = std_::integral_constant) <= kStorageSize)>; -protected: - static void bad_construct_copy(void *lambda, const void *source) { - t_assert(!"base::lambda bad_construct_copy() called!"); - } + protected: + static void bad_construct_copy(void *lambda, const void *source) { + t_assert(!"base::lambda bad_construct_copy() called!"); + } -}; + }; -template -struct lambda_wrap_empty : public lambda_wrap_helper_base { - static void construct_copy_other_method(void *lambda, const void *source) { - } - static void construct_move_other_method(void *lambda, void *source) { - } - static Return call_method(const void *lambda, Args... args) { - t_assert(!"base::lambda empty call_method() called!"); - return Return(); - } - static void destruct_method(const void *lambda) { - } - lambda_wrap_empty() : lambda_wrap_helper_base( - &lambda_wrap_empty::construct_copy_other_method, - &lambda_wrap_empty::construct_move_other_method, - &lambda_wrap_empty::call_method, - &lambda_wrap_empty::destruct_method) { - } + template + struct lambda_wrap_empty : public lambda_wrap_helper_base { + static void construct_copy_other_method(void *lambda, const void *source) { + } + static void construct_move_other_method(void *lambda, void *source) { + } + static Return call_method(const void *lambda, Args... args) { + t_assert(!"base::lambda empty call_method() called!"); + return Return(); + } + static void destruct_method(const void *lambda) { + } + lambda_wrap_empty() : lambda_wrap_helper_base( + &lambda_wrap_empty::construct_copy_other_method, + &lambda_wrap_empty::construct_move_other_method, + &lambda_wrap_empty::call_method, + &lambda_wrap_empty::destruct_method) { + } - static const lambda_wrap_empty instance; + static const lambda_wrap_empty instance; -}; + }; -template -const lambda_wrap_empty lambda_wrap_empty::instance = {}; + template + const lambda_wrap_empty lambda_wrap_empty::instance = {}; -template struct lambda_wrap_helper_move_impl; + template struct lambda_wrap_helper_move_impl; -// -// Disable large lambda support. -// If you really need it, just store data in some std_::unique_ptr. -// -//template -//struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { -// using JustLambda = std_::decay_simple_t; -// using LambdaPtr = std_::unique_ptr; -// using Parent = lambda_wrap_helper_base; -// static void construct_move_other_method(void *lambda, void *source) { -// auto source_lambda = static_cast(source); -// new (lambda) LambdaPtr(std_::move(*source_lambda)); -// } -// static void construct_move_lambda_method(void *lambda, void *source) { -// auto source_lambda = static_cast(source); -// new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); -// } -// static Return call_method(const void *lambda, Args... args) { -// return (**static_cast(lambda))(std_::forward(args)...); -// } -// static void destruct_method(const void *lambda) { -// static_cast(lambda)->~LambdaPtr(); -// } -// lambda_wrap_helper_move_impl() : Parent( -// &Parent::bad_construct_copy, -// &lambda_wrap_helper_move_impl::construct_move_other_method, -// &lambda_wrap_helper_move_impl::call_method, -// &lambda_wrap_helper_move_impl::destruct_method) { -// } -// -//protected: -// lambda_wrap_helper_move_impl( -// typename Parent::construct_copy_other_type construct_copy_other -// ) : Parent( -// construct_copy_other, -// &lambda_wrap_helper_move_impl::construct_move_other_method, -// &lambda_wrap_helper_move_impl::call_method, -// &lambda_wrap_helper_move_impl::destruct_method) { -// } -// -//}; + // + // Disable large lambda support. + // If you really need it, just store data in some std_::unique_ptr. + // + //template + //struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { + // using JustLambda = std_::decay_simple_t; + // using LambdaPtr = std_::unique_ptr; + // using Parent = lambda_wrap_helper_base; + // static void construct_move_other_method(void *lambda, void *source) { + // auto source_lambda = static_cast(source); + // new (lambda) LambdaPtr(std_::move(*source_lambda)); + // } + // static void construct_move_lambda_method(void *lambda, void *source) { + // auto source_lambda = static_cast(source); + // new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); + // } + // static Return call_method(const void *lambda, Args... args) { + // return (**static_cast(lambda))(std_::forward(args)...); + // } + // static void destruct_method(const void *lambda) { + // static_cast(lambda)->~LambdaPtr(); + // } + // lambda_wrap_helper_move_impl() : Parent( + // &Parent::bad_construct_copy, + // &lambda_wrap_helper_move_impl::construct_move_other_method, + // &lambda_wrap_helper_move_impl::call_method, + // &lambda_wrap_helper_move_impl::destruct_method) { + // } + // + //protected: + // lambda_wrap_helper_move_impl( + // typename Parent::construct_copy_other_type construct_copy_other + // ) : Parent( + // construct_copy_other, + // &lambda_wrap_helper_move_impl::construct_move_other_method, + // &lambda_wrap_helper_move_impl::call_method, + // &lambda_wrap_helper_move_impl::destruct_method) { + // } + // + //}; -template -struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { - using JustLambda = std_::decay_simple_t; - using Parent = lambda_wrap_helper_base; - static void construct_move_other_method(void *lambda, void *source) { - auto source_lambda = static_cast(source); - new (lambda) JustLambda(static_cast(*source_lambda)); - } - static void construct_move_lambda_method(void *lambda, void *source) { - static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); - auto space = sizeof(JustLambda); - auto aligned = std_::align(alignof(JustLambda), space, lambda, space); - t_assert(aligned == lambda); - auto source_lambda = static_cast(source); - new (lambda) JustLambda(static_cast(*source_lambda)); - } - static Return call_method(const void *lambda, Args... args) { - return (*static_cast(lambda))(std_::forward(args)...); - } - static void destruct_method(const void *lambda) { - static_cast(lambda)->~JustLambda(); - } - lambda_wrap_helper_move_impl() : Parent( - &Parent::bad_construct_copy, - &lambda_wrap_helper_move_impl::construct_move_other_method, - &lambda_wrap_helper_move_impl::call_method, - &lambda_wrap_helper_move_impl::destruct_method) { - } + template + struct lambda_wrap_helper_move_impl : public lambda_wrap_helper_base { + using JustLambda = std_::decay_simple_t; + using Parent = lambda_wrap_helper_base; + static void construct_move_other_method(void *lambda, void *source) { + auto source_lambda = static_cast(source); + new (lambda) JustLambda(static_cast(*source_lambda)); + } + static void construct_move_lambda_method(void *lambda, void *source) { + static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); + auto space = sizeof(JustLambda); + auto aligned = std_::align(alignof(JustLambda), space, lambda, space); + t_assert(aligned == lambda); + auto source_lambda = static_cast(source); + new (lambda) JustLambda(static_cast(*source_lambda)); + } + static Return call_method(const void *lambda, Args... args) { + return (*static_cast(lambda))(std_::forward(args)...); + } + static void destruct_method(const void *lambda) { + static_cast(lambda)->~JustLambda(); + } + lambda_wrap_helper_move_impl() : Parent( + &Parent::bad_construct_copy, + &lambda_wrap_helper_move_impl::construct_move_other_method, + &lambda_wrap_helper_move_impl::call_method, + &lambda_wrap_helper_move_impl::destruct_method) { + } -protected: - lambda_wrap_helper_move_impl( - typename Parent::construct_copy_other_type construct_copy_other - ) : Parent( - construct_copy_other, - &lambda_wrap_helper_move_impl::construct_move_other_method, - &lambda_wrap_helper_move_impl::call_method, - &lambda_wrap_helper_move_impl::destruct_method) { - } + protected: + lambda_wrap_helper_move_impl( + typename Parent::construct_copy_other_type construct_copy_other + ) : Parent( + construct_copy_other, + &lambda_wrap_helper_move_impl::construct_move_other_method, + &lambda_wrap_helper_move_impl::call_method, + &lambda_wrap_helper_move_impl::destruct_method) { + } -}; + }; -template -struct lambda_wrap_helper_move : public lambda_wrap_helper_move_impl::template IsLarge - , Return, Args...> { - static const lambda_wrap_helper_move instance; -}; + template + struct lambda_wrap_helper_move : public lambda_wrap_helper_move_impl::template IsLarge + , Return, Args...> { + static const lambda_wrap_helper_move instance; + }; -template -const lambda_wrap_helper_move lambda_wrap_helper_move::instance = {}; + template + const lambda_wrap_helper_move lambda_wrap_helper_move::instance = {}; -template struct lambda_wrap_helper_copy_impl; + template struct lambda_wrap_helper_copy_impl; -// -// Disable large lambda support. -// If you really need it, just store data in some QSharedPointer. -// -//template -//struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { -// using JustLambda = std_::decay_simple_t; -// using LambdaPtr = std_::unique_ptr; -// using Parent = lambda_wrap_helper_move_impl; -// static void construct_copy_other_method(void *lambda, const void *source) { -// auto source_lambda = static_cast(source); -// new (lambda) LambdaPtr(std_::make_unique(*source_lambda->get())); -// } -// static void construct_copy_lambda_method(void *lambda, const void *source) { -// auto source_lambda = static_cast(source); -// new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); -// } -// lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) { -// } -// -//}; + // + // Disable large lambda support. + // If you really need it, just store data in some QSharedPointer. + // + //template + //struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { + // using JustLambda = std_::decay_simple_t; + // using LambdaPtr = std_::unique_ptr; + // using Parent = lambda_wrap_helper_move_impl; + // static void construct_copy_other_method(void *lambda, const void *source) { + // auto source_lambda = static_cast(source); + // new (lambda) LambdaPtr(std_::make_unique(*source_lambda->get())); + // } + // static void construct_copy_lambda_method(void *lambda, const void *source) { + // auto source_lambda = static_cast(source); + // new (lambda) LambdaPtr(std_::make_unique(static_cast(*source_lambda))); + // } + // lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) { + // } + // + //}; -template -struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { - using JustLambda = std_::decay_simple_t; - using Parent = lambda_wrap_helper_move_impl; - static void construct_copy_other_method(void *lambda, const void *source) { - auto source_lambda = static_cast(source); - new (lambda) JustLambda(static_cast(*source_lambda)); - } - static void construct_copy_lambda_method(void *lambda, const void *source) { - static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); - auto space = sizeof(JustLambda); - auto aligned = std_::align(alignof(JustLambda), space, lambda, space); - t_assert(aligned == lambda); - auto source_lambda = static_cast(source); - new (lambda) JustLambda(static_cast(*source_lambda)); - } - lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) { - } + template + struct lambda_wrap_helper_copy_impl : public lambda_wrap_helper_move_impl { + using JustLambda = std_::decay_simple_t; + using Parent = lambda_wrap_helper_move_impl; + static void construct_copy_other_method(void *lambda, const void *source) { + auto source_lambda = static_cast(source); + new (lambda) JustLambda(static_cast(*source_lambda)); + } + static void construct_copy_lambda_method(void *lambda, const void *source) { + static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); + auto space = sizeof(JustLambda); + auto aligned = std_::align(alignof(JustLambda), space, lambda, space); + t_assert(aligned == lambda); + auto source_lambda = static_cast(source); + new (lambda) JustLambda(static_cast(*source_lambda)); + } + lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) { + } -}; + }; -template -struct lambda_wrap_helper_copy : public lambda_wrap_helper_copy_impl::template IsLarge - , Return, Args...> { - static const lambda_wrap_helper_copy instance; -}; + template + struct lambda_wrap_helper_copy : public lambda_wrap_helper_copy_impl::template IsLarge + , Return, Args...> { + static const lambda_wrap_helper_copy instance; + }; -template -const lambda_wrap_helper_copy lambda_wrap_helper_copy::instance = {}; + template + const lambda_wrap_helper_copy lambda_wrap_helper_copy::instance = {}; } // namespace internal @@ -262,6 +262,8 @@ class lambda { using IsRvalue = std_::enable_if_t::value>; public: + using return_type = Return; + lambda() : helper_(&EmptyHelper::instance) { } @@ -387,6 +389,116 @@ public: }; +// Get lambda type from a lambda template parameter. + +namespace internal { + + template + struct lambda_type_helper; + + template + struct lambda_type_helper { + using type = lambda; + }; + +} // namespace internal + +template +using lambda_type = typename internal::lambda_type_helper::type; + +// Guard lambda call by one or many QObject* weak pointers. + +namespace internal { + +template +class lambda_guard_creator; + +template +class lambda_guard_data { +public: + using return_type = typename lambda_type::return_type; + + template + lambda_guard_data(PointersAndLambda&&... qobjectsAndLambda) : _lambda(init(_pointers, std_::forward(qobjectsAndLambda)...)) { + } + + template + inline return_type operator()(Args... args) const { + for (int i = 0; i != N; ++i) { + if (!_pointers[i]) { + return return_type(); + } + } + return _lambda(std_::forward(args)...); + } + +private: + template + Lambda init(QPointer *pointers, QObject *qobject, PointersAndLambda&&... qobjectsAndLambda) { + *pointers = qobject; + return init(++pointers, std_::forward(qobjectsAndLambda)...); + } + Lambda init(QPointer *pointers, Lambda &&lambda) { + return std_::move(lambda); + } + + QPointer _pointers[N]; + Lambda _lambda; + +}; + +template +class lambda_guard { +public: + using return_type = typename lambda_type::return_type; + + template + lambda_guard(PointersAndLambda&&... qobjectsAndLambda) : _data(std_::make_unique>(std_::forward(qobjectsAndLambda)...)) { + static_assert(sizeof...(PointersAndLambda) == N + 1, "Wrong argument count!"); + } + + template + inline return_type operator()(Args... args) const { + return (*_data)(std_::forward(args)...); + } + +private: + std_::unique_ptr> _data; + +}; + +template +struct lambda_guard_type; + +template +struct lambda_guard_type { + using type = typename lambda_guard_type::type; +}; + +template +struct lambda_guard_type { + using type = lambda_guard; +}; + +template +struct lambda_guard_type_helper { + static constexpr int N = sizeof...(PointersAndLambda); + using type = typename lambda_guard_type::type; +}; + +template +using lambda_guard_t = typename lambda_guard_type_helper::type; + +} // namespace internal + +template +inline internal::lambda_guard_t lambda_guarded(PointersAndLambda&&... qobjectsAndLambda) { + static_assert(sizeof...(PointersAndLambda) > 0, "Lambda should be passed here."); + return internal::lambda_guard_t(std_::forward(qobjectsAndLambda)...); +} + +// Pass lambda instead of a Qt void() slot. + class lambda_slot_wrap : public QObject { Q_OBJECT diff --git a/Telegram/SourceFiles/core/observer.h b/Telegram/SourceFiles/core/observer.h index e5e425ed54..5a432ed762 100644 --- a/Telegram/SourceFiles/core/observer.h +++ b/Telegram/SourceFiles/core/observer.h @@ -120,7 +120,7 @@ public: if (!_data) { _data = MakeShared>(this); } - return _data->append(std_::forward(handler)); + return _data->append(std_::move(handler)); } private: @@ -169,7 +169,7 @@ public: } Subscription append(Handler &&handler) { - auto node = new Node(_observable->_data, std_::forward(handler)); + auto node = new Node(_observable->_data, std_::move(handler)); if (_begin) { _end->next = node; node->prev = _end; diff --git a/Telegram/SourceFiles/core/stl_subset.h b/Telegram/SourceFiles/core/stl_subset.h index 41d4f00072..c87ccc604a 100644 --- a/Telegram/SourceFiles/core/stl_subset.h +++ b/Telegram/SourceFiles/core/stl_subset.h @@ -158,14 +158,9 @@ template struct add_const { using type = const T; }; + template using add_const_t = typename add_const::type; -template -constexpr add_const_t &as_const(T& t) noexcept { - return t; -} -template -void as_const(const T&&) = delete; // This is not full unique_ptr, but at least with std interface. template diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 35ae9f7a64..8c6684f3ba 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -47,6 +47,11 @@ inline constexpr D up_cast_helper(std_::false_type, T object) { return nullptr; } +template +constexpr std_::add_const_t &any_as_const(T &&value) noexcept { + return value; +} + } // namespace internal template @@ -83,6 +88,12 @@ scope_guard_helper scope_guard(Lambda on_scope_exit) { } // namespace base +// using for_const instead of plain range-based for loop to ensure usage of const_iterator +// it is important for the copy-on-write Qt containers +// if you have "QVector v" then "for (T * const p : v)" will still call QVector::detach(), +// while "for_const (T *p, v)" won't and "for_const (T *&p, v)" won't compile +#define for_const(range_declaration, range_expression) for (range_declaration : base::internal::any_as_const(range_expression)) + template inline QFlags qFlags(Enum v) { return QFlags(v); @@ -352,11 +363,6 @@ enum DBIConnectionType { dbictTcpProxy = 3, }; -enum DBIDefaultAttach { - dbidaDocument = 0, - dbidaPhoto = 1, -}; - struct ProxyData { QString host; uint32 port = 0; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index c35b27dadc..decb79bc4a 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019009ULL) +#define BETA_VERSION_MACRO (10019010ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index 1487168ab5..2b2fc30467 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -29,10 +29,10 @@ FileUploader::FileUploader() : sentSize(0) { connect(&killSessionsTimer, SIGNAL(timeout()), this, SLOT(killSessions())); } -void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &media) { - if (media.type == PreparePhoto) { +void FileUploader::uploadMedia(const FullMsgId &msgId, const SendMediaReady &media) { + if (media.type == SendMediaType::Photo) { App::feedPhoto(media.photo, media.photoThumbs); - } else if (media.type == PrepareDocument || media.type == PrepareAudio) { + } else if (media.type == SendMediaType::File || media.type == SendMediaType::Audio) { DocumentData *document; if (media.photoThumbs.isEmpty()) { document = App::feedDocument(media.document); @@ -52,10 +52,10 @@ void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &me } void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file) { - if (file->type == PreparePhoto) { + if (file->type == SendMediaType::Photo) { PhotoData *photo = App::feedPhoto(file->photo, file->photoThumbs); photo->uploadingData = new PhotoData::UploadingData(file->partssize); - } else if (file->type == PrepareDocument || file->type == PrepareAudio) { + } else if (file->type == SendMediaType::File || file->type == SendMediaType::Audio) { DocumentData *document; if (file->thumb.isNull()) { document = App::feedDocument(file->document); @@ -77,9 +77,9 @@ void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file) void FileUploader::currentFailed() { Queue::iterator j = queue.find(uploading); if (j != queue.end()) { - if (j->type() == PreparePhoto) { + if (j->type() == SendMediaType::Photo) { emit photoFailed(j.key()); - } else if (j->type() == PrepareDocument) { + } else if (j->type() == SendMediaType::File) { DocumentData *doc = App::document(j->id()); if (doc->status == FileUploading) { doc->status = FileUploadFailed; @@ -135,15 +135,15 @@ void FileUploader::sendNext() { } } - UploadFileParts &parts(i->file ? (i->type() == PreparePhoto ? i->file->fileparts : i->file->thumbparts) : i->media.parts); - uint64 partsOfId(i->file ? (i->type() == PreparePhoto ? i->file->id : i->file->thumbId) : i->media.thumbId); + UploadFileParts &parts(i->file ? (i->type() == SendMediaType::Photo ? i->file->fileparts : i->file->thumbparts) : i->media.parts); + uint64 partsOfId(i->file ? (i->type() == SendMediaType::Photo ? i->file->id : i->file->thumbId) : i->media.thumbId); if (parts.isEmpty()) { if (i->docSentParts >= i->docPartsCount) { if (requestsSent.isEmpty() && docRequestsSent.isEmpty()) { bool silent = i->file && i->file->to.silent; - if (i->type() == PreparePhoto) { + if (i->type() == SendMediaType::Photo) { emit photoReady(uploading, silent, MTP_inputFile(MTP_long(i->id()), MTP_int(i->partsCount), MTP_string(i->filename()), MTP_bytes(i->file ? i->file->filemd5 : i->media.jpeg_md5))); - } else if (i->type() == PrepareDocument || i->type() == PrepareAudio) { + } else if (i->type() == SendMediaType::File || i->type() == SendMediaType::Audio) { QByteArray docMd5(32, Qt::Uninitialized); hashMd5Hex(i->md5Hash.result(), docMd5.data()); @@ -177,7 +177,7 @@ void FileUploader::sendNext() { } } else { toSend = content.mid(i->docSentParts * i->docPartSize, i->docPartSize); - if ((i->type() == PrepareDocument || i->type() == PrepareAudio) && i->docSentParts <= UseBigFilesFrom) { + if ((i->type() == SendMediaType::File || i->type() == SendMediaType::Audio) && i->docSentParts <= UseBigFilesFrom) { i->md5Hash.feed(toSend.constData(), toSend.size()); } } @@ -282,7 +282,7 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } sentSize -= sentPartSize; sentSizes[dc] -= sentPartSize; - if (k->type() == PreparePhoto) { + if (k->type() == SendMediaType::Photo) { k->fileSentSize += sentPartSize; PhotoData *photo = App::photo(k->id()); if (photo->uploading() && k->file) { @@ -290,7 +290,7 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { photo->uploadingData->offset = k->fileSentSize; } emit photoProgress(k.key()); - } else if (k->type() == PrepareDocument || k->type() == PrepareAudio) { + } else if (k->type() == SendMediaType::File || k->type() == SendMediaType::Audio) { DocumentData *doc = App::document(k->id()); if (doc->uploading()) { doc->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize; diff --git a/Telegram/SourceFiles/fileuploader.h b/Telegram/SourceFiles/fileuploader.h index 5ae1c6dbcd..68d0b13287 100644 --- a/Telegram/SourceFiles/fileuploader.h +++ b/Telegram/SourceFiles/fileuploader.h @@ -26,9 +26,8 @@ class FileUploader : public QObject, public RPCSender { Q_OBJECT public: - FileUploader(); - void uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &image); + void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image); void upload(const FullMsgId &msgId, const FileLoadResultPtr &file); int32 currentOffset(const FullMsgId &msgId) const; // -1 means file not found @@ -41,13 +40,11 @@ public: void clear(); public slots: - void unpause(); void sendNext(); void killSessions(); signals: - void photoReady(const FullMsgId &msgId, bool silent, const MTPInputFile &file); void documentReady(const FullMsgId &msgId, bool silent, const MTPInputFile &file); void thumbDocumentReady(const FullMsgId &msgId, bool silent, const MTPInputFile &file, const MTPInputFile &thumb); @@ -59,19 +56,18 @@ signals: void documentFailed(const FullMsgId &msgId); private: - struct File { - File(const ReadyLocalMedia &media) : media(media), docSentParts(0) { + File(const SendMediaReady &media) : media(media), docSentParts(0) { partsCount = media.parts.size(); - if (type() == PrepareDocument || type() == PrepareAudio) { + if (type() == SendMediaType::File || type() == SendMediaType::Audio) { setDocSize(media.file.isEmpty() ? media.data.size() : media.filesize); } else { docSize = docPartSize = docPartsCount = 0; } } File(const FileLoadResultPtr &file) : file(file), docSentParts(0) { - partsCount = (type() == PreparePhoto) ? file->fileparts.size() : file->thumbparts.size(); - if (type() == PrepareDocument || type() == PrepareAudio) { + partsCount = (type() == SendMediaType::Photo) ? file->fileparts.size() : file->thumbparts.size(); + if (type() == SendMediaType::File || type() == SendMediaType::Audio) { setDocSize(file->filesize); } else { docSize = docPartSize = docPartsCount = 0; @@ -98,14 +94,14 @@ private: } FileLoadResultPtr file; - ReadyLocalMedia media; + SendMediaReady media; int32 partsCount; mutable int32 fileSentSize; uint64 id() const { return file ? file->id : media.id; } - PrepareMediaType type() const { + SendMediaType type() const { return file ? file->type : media.type; } uint64 thumbId() const { diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 3fecef958d..e3b8d99832 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -235,10 +235,6 @@ historyAttach: IconButton(historySend) { color: windowBgOver; } } -historyAttachFileIcon: icon {{ "media_type_file", historyComposeIconFg }}; -historyAttachFileIconOver: icon {{ "media_type_file", historyComposeIconFgOver }}; -historyAttachPhotoIcon: icon {{ "media_type_photo", historyComposeIconFg }}; -historyAttachPhotoIconOver: icon {{ "media_type_photo", historyComposeIconFgOver }}; historyAttachEmoji: IconButton(historyAttach) { icon: icon {{ "send_control_emoji", historyComposeIconFg }}; @@ -283,17 +279,6 @@ historyRecordFont: font(13px); historyRecordDurationFg: #000000; historyRecordTextTop: 14px; -historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { - menu: Menu(defaultMenu) { - itemIconPosition: point(13px, 6px); - itemPadding: margins(54px, 11px, 54px, 11px); - } -} -historyMediaTypeFile: icon {{ "media_type_file", menuIconFg, point(2px, 2px) }}; -historyMediaTypeFileOver: icon {{ "media_type_file", menuIconFgOver, point(2px, 2px) }}; -historyMediaTypePhoto: icon {{ "media_type_photo", menuIconFg, point(2px, 2px) }}; -historyMediaTypePhotoOver: icon {{ "media_type_photo", menuIconFgOver, point(2px, 2px) }}; - historySilentToggle: IconButton(historyBotKeyboardShow) { icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; iconOver: icon {{ "send_control_silent_off", historyComposeIconFgOver }}; diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index a5eadc849a..00c69cb075 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -116,8 +116,8 @@ void DragArea::dragLeaveEvent(QDragLeaveEvent *e) { void DragArea::dropEvent(QDropEvent *e) { static_cast(parentWidget())->dropEvent(e); - if (e->isAccepted()) { - emit dropped(e->mimeData()); + if (e->isAccepted() && _droppedCallback) { + _droppedCallback(e->mimeData()); } } diff --git a/Telegram/SourceFiles/history/history_drag_area.h b/Telegram/SourceFiles/history/history_drag_area.h index 04bdb38ab2..fc4f11afdd 100644 --- a/Telegram/SourceFiles/history/history_drag_area.h +++ b/Telegram/SourceFiles/history/history_drag_area.h @@ -48,6 +48,10 @@ public: void hideFast(); + void setDroppedCallback(base::lambda &&callback) { + _droppedCallback = std_::move(callback); + } + protected: void paintEvent(QPaintEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; @@ -56,9 +60,6 @@ protected: void dropEvent(QDropEvent *e) override; void dragMoveEvent(QDragMoveEvent *e) override; -signals: - void dropped(const QMimeData *data); - public slots: void hideStart(); void hideFinish(); @@ -67,6 +68,7 @@ public slots: private: bool _hiding, _in; + base::lambda _droppedCallback; anim::fvalue a_opacity; anim::fvalue a_colorDrop; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 747bcb2acf..ca051753f9 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -99,7 +99,7 @@ QString ReplyMarkupClickHandler::buttonText() const { ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s) : _item(item) , _a_selected(animation(this, &ReplyKeyboard::step_selected)) - , _st(std_::forward(s)) { + , _st(std_::move(s)) { if (auto markup = item->Get()) { _rows.reserve(markup->rows.size()); for (int i = 0, l = markup->rows.size(); i != l; ++i) { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 861e502729..d1ee320b69 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_window.h" #include "styles/style_boxes.h" #include "boxes/confirmbox.h" -#include "boxes/photosendbox.h" +#include "boxes/send_files_box.h" #include "boxes/sharebox.h" #include "ui/filedialog.h" #include "ui/toast/toast.h" @@ -57,6 +57,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "observer_peer.h" #include "core/qthelp_regex.h" #include "ui/widgets/popup_menu.h" +#include "platform/platform_file_dialog.h" namespace { @@ -2392,28 +2393,8 @@ bool MessageField::canInsertFromMimeData(const QMimeData *source) const { } void MessageField::insertFromMimeData(const QMimeData *source) { - if (source->hasUrls()) { - int32 files = 0; - QUrl url; - for (int32 i = 0; i < source->urls().size(); ++i) { - if (source->urls().at(i).isLocalFile()) { - url = source->urls().at(i); - ++files; - } - } - if (files == 1) { - history->uploadFile(url.toLocalFile(), PrepareAuto, FileLoadAlwaysConfirm); - return; - } - if (files > 1) return; - //if (files > 1) return uploadFiles(files, PrepareAuto); // multiple confirm with "compressed" checkbox - } - if (source->hasImage()) { - QImage img = qvariant_cast(source->imageData()); - if (!img.isNull()) { - history->uploadImage(img, PrepareAuto, FileLoadAlwaysConfirm, source->text()); - return; - } + if (history->confirmSendingFiles(source, CompressConfirm::Auto, source->text())) { + return; } FlatTextarea::insertFromMimeData(source); } @@ -2896,33 +2877,30 @@ bool HistoryHider::offerPeer(PeerId peer) { if (_sharedContact) { phrase = lng_forward_share_contact(lt_recipient, recipient); } else if (_sendPath) { - if (cSendPaths().size() > 1) { - phrase = lng_forward_send_files_confirm(lt_recipient, recipient); - } else { - QString name(QFileInfo(cSendPaths().front()).fileName()); - if (name.size() > 10) { - name = name.mid(0, 8) + '.' + '.'; - } - phrase = lng_forward_send_file_confirm(lt_name, name, lt_recipient, recipient); - } - } else if (!_shareUrl.isEmpty()) { - PeerId to = _offered->id; + auto toId = _offered->id; _offered = nullptr; - if (parent()->onShareUrl(to, _shareUrl, _shareText)) { + if (parent()->onSendPaths(toId)) { + startHide(); + } + return false; + } else if (!_shareUrl.isEmpty()) { + auto toId = _offered->id; + _offered = nullptr; + if (parent()->onShareUrl(toId, _shareUrl, _shareText)) { startHide(); } return false; } else if (!_botAndQuery.isEmpty()) { - PeerId to = _offered->id; + auto toId = _offered->id; _offered = nullptr; - if (parent()->onInlineSwitchChosen(to, _botAndQuery)) { + if (parent()->onInlineSwitchChosen(toId, _botAndQuery)) { startHide(); } return false; } else { - PeerId to = _offered->id; + auto toId = _offered->id; _offered = nullptr; - if (parent()->onForward(to, _forwardSelected ? ForwardSelectedMessages : ForwardContextMessage)) { + if (parent()->onForward(toId, _forwardSelected ? ForwardSelectedMessages : ForwardContextMessage)) { startHide(); } return false; @@ -3079,7 +3057,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel))) , _kbScroll(this, st::botKbScroll) , _keyboard(this) -, _attachType(this, st::historyAttachDropdownMenu) , _emojiPan(this) , _attachDragDocument(this) , _attachDragPhoto(this) @@ -3101,18 +3078,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel())); connect(_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute())); connect(_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange())); - _attachToggle->setClickedCallback([this] { - if (cDefaultAttach() == dbidaPhoto) { - onPhotoSelect(); - } else { - onDocumentSelect(); - } - }); - if (cDefaultAttach() == dbidaPhoto) { - _attachToggle->setIcon(&st::historyAttachPhotoIcon, &st::historyAttachPhotoIconOver); - } else { - _attachToggle->setIcon(&st::historyAttachFileIcon, &st::historyAttachFileIconOver); - } connect(_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); connect(_field, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed())); @@ -3136,6 +3101,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(audioCapture(), SIGNAL(done(QByteArray,VoiceWaveform,qint32)), this, SLOT(onRecordDone(QByteArray,VoiceWaveform,qint32))); } + _attachToggle->setClickedCallback([this] { chooseAttach(); }); + subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) { + notifyFileQueryUpdated(update); + }); + _updateHistoryItems.setSingleShot(true); connect(&_updateHistoryItems, SIGNAL(timeout()), this, SLOT(onUpdateHistoryItems())); @@ -3195,25 +3165,20 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _silent->hide(); _botCommandStart->hide(); - _attachType->setOrigin(Ui::PanelAnimation::Origin::BottomLeft); - _attachToggle->installEventFilter(_attachType); _attachEmoji->installEventFilter(_emojiPan); connect(_botKeyboardShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(_botKeyboardHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(_botCommandStart, SIGNAL(clicked()), this, SLOT(onCmdStart())); - _attachType->addAction(lang(lng_attach_file), this, SLOT(onDocumentSelect()), &st::historyMediaTypeFile, &st::historyMediaTypeFileOver); - _attachType->addAction(lang(lng_attach_photo), this, SLOT(onPhotoSelect()), &st::historyMediaTypePhoto, &st::historyMediaTypePhotoOver); - _attachType->hide(); _emojiPan->hide(); _attachDragDocument->hide(); _attachDragPhoto->hide(); _topShadow->hide(); - connect(_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*))); - connect(_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*))); + _attachDragDocument->setDroppedCallback([this](const QMimeData *data) { confirmSendingFiles(data, CompressConfirm::No); }); + _attachDragPhoto->setDroppedCallback([this](const QMimeData *data) { confirmSendingFiles(data, CompressConfirm::Yes); }); connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField())); @@ -3567,11 +3532,13 @@ void HistoryWidget::onRecordError() { } void HistoryWidget::onRecordDone(QByteArray result, VoiceWaveform waveform, qint32 samples) { - if (!_peer || result.isEmpty()) return; + if (!canWriteMessage() || result.isEmpty()) return; App::wnd()->activateWindow(); - int32 duration = samples / AudioVoiceMsgFrequency; - _fileLoader.addTask(new FileLoadTask(result, duration, waveform, FileLoadTo(_peer->id, _silent->checked(), replyToId()))); + auto duration = samples / AudioVoiceMsgFrequency; + auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); + auto caption = QString(); + _fileLoader.addTask(MakeShared(result, duration, waveform, to, caption)); cancelReplyAfterMediaSend(lastForceReplyReplied()); } @@ -4460,7 +4427,6 @@ void HistoryWidget::updateNotifySettings() { bool HistoryWidget::contentOverlapped(const QRect &globalRect) { return (_attachDragDocument->overlaps(globalRect) || _attachDragPhoto->overlaps(globalRect) || - _attachType->overlaps(globalRect) || _fieldAutocomplete->overlaps(globalRect) || _emojiPan->overlaps(globalRect)); } @@ -4563,9 +4529,8 @@ bool HistoryWidget::reportSpamSettingFail(const RPCError &error, mtpRequestId re } bool HistoryWidget::canWriteMessage() const { - if (!_history || _a_show.animating()) return false; + if (!_history || !_canSendMessages) return false; if (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart()) return false; - if (!_canSendMessages) return false; return true; } @@ -4594,7 +4559,6 @@ void HistoryWidget::updateControlsVisibility() { _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _attachType->hide(); _emojiPan->hide(); if (_pinnedBar) { _pinnedBar->cancel->hide(); @@ -4654,7 +4618,6 @@ void HistoryWidget::updateControlsVisibility() { _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _attachType->hide(); _emojiPan->hide(); if (!_field->isHidden()) { _field->hide(); @@ -4779,7 +4742,6 @@ void HistoryWidget::updateControlsVisibility() { _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _attachType->hide(); _emojiPan->hide(); _kbScroll->hide(); if (!_field->isHidden()) { @@ -5297,7 +5259,6 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp void HistoryWidget::hideSelectorControlsAnimated() { _fieldAutocomplete->hideAnimated(); - _attachType->hideAnimated(); _emojiPan->hideAnimated(); } @@ -5481,17 +5442,6 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const cancelReplyAfterMediaSend(lastKeyboardUsed); } -void HistoryWidget::onSendPaths(const PeerId &peer) { - Ui::showPeerHistory(peer, ShowAtTheEndMsgId); - if (!_history) return; - - if (cSendPaths().size() == 1) { - uploadFile(cSendPaths().at(0), PrepareAuto); - } else { - uploadFiles(cSendPaths(), PrepareDocument); - } -} - History *HistoryWidget::history() const { return _history; } @@ -5645,62 +5595,49 @@ void HistoryWidget::step_recording(float64 ms, bool timer) { if (timer) update(_attachToggle->geometry()); } -void HistoryWidget::onPhotoSelect() { +void HistoryWidget::chooseAttach() { if (!_history) return; - _attachToggle->setIcon(&st::historyAttachPhotoIcon, &st::historyAttachPhotoIconOver); - _attachType->hideFast(); + auto photoExtensions = cPhotoExtensions(); + auto imageExtensions = cImgExtensions(); + auto filter = filedialogAllFilesFilter() + qsl(";;Image files (*") + imageExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(")"); - if (cDefaultAttach() != dbidaPhoto) { - cSetDefaultAttach(dbidaPhoto); - Local::writeUserSettings(); - } - - QStringList photoExtensions(cPhotoExtensions()); - QStringList imgExtensions(cImgExtensions()); - QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter()); - - QStringList files; - QByteArray content; - if (filedialogGetOpenFiles(files, content, lang(lng_choose_images), filter)) { - if (!content.isEmpty()) { - uploadFileContent(content, PreparePhoto); - } else { - uploadFiles(files, PreparePhoto); - } - } + _attachFilesQueryId = FileDialog::queryReadFiles(lang(lng_choose_files), filter); } -void HistoryWidget::onDocumentSelect() { - if (!_history) return; +void HistoryWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) { + if (_attachFilesQueryId != update.queryId) { + return; + } + _attachFilesQueryId = 0; - _attachToggle->setIcon(&st::historyAttachFileIcon, &st::historyAttachFileIconOver); - _attachType->hideFast(); - - if (cDefaultAttach() != dbidaDocument) { - cSetDefaultAttach(dbidaDocument); - Local::writeUserSettings(); + if (update.filePaths.isEmpty() && update.remoteContent.isEmpty()) { + return; } - QStringList photoExtensions(cPhotoExtensions()); - QStringList imgExtensions(cImgExtensions()); - QString filter(filedialogAllFilesFilter() + qsl(";;Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(")")); - - QStringList files; - QByteArray content; - if (filedialogGetOpenFiles(files, content, lang(lng_choose_images), filter)) { - if (!content.isEmpty()) { - uploadFileContent(content, PrepareDocument); + if (!update.remoteContent.isEmpty()) { + auto animated = false; + auto image = App::readImage(update.remoteContent, nullptr, false, &animated); + if (!image.isNull() && !animated) { + confirmSendingFiles(image, update.remoteContent); } else { - uploadFiles(files, PrepareDocument); + uploadFile(update.remoteContent, SendMediaType::File); + } + } else { + auto lists = getSendingFilesLists(update.filePaths); + if (lists.allFilesArePhotos) { + confirmSendingFiles(lists); + } else { + validateSendingFiles(lists, [this](const QStringList &files) { + uploadFiles(files, SendMediaType::File); + return true; + }); } } } void HistoryWidget::dragEnterEvent(QDragEnterEvent *e) { - if (!_history) return; - - if (_peer && !_canSendMessages) return; + if (!_history || !_canSendMessages) return; _attachDrag = getDragState(e->mimeData()); updateDragAreas(); @@ -5926,7 +5863,7 @@ bool HistoryWidget::botCallbackFail(BotCallbackInfo info, const RPCError &error, } bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { - if (!_history || !canWriteMessage()) return false; + if (!canWriteMessage()) return false; bool insertingInlineBot = !cmd.isEmpty() && (cmd.at(0) == '@'); QString toInsert = cmd; @@ -6002,17 +5939,17 @@ DragState HistoryWidget::getDragState(const QMimeData *d) { for (QList::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) { if (!i->isLocalFile()) return DragStateNone; - auto file = psConvertFileUrl(*i); + auto file = Platform::FileDialog::UrlToLocal(*i); QFileInfo info(file); if (info.isDir()) return DragStateNone; quint64 s = info.size(); - if (s >= MaxUploadDocumentSize) { + if (s > App::kFileSizeLimit) { return DragStateNone; } if (allAreSmallImages) { - if (s >= MaxUploadPhotoSize) { + if (s > App::kImageSizeLimit) { allAreSmallImages = false; } else { bool foundImageExtension = false; @@ -6156,57 +6093,8 @@ void HistoryWidget::dropEvent(QDropEvent *e) { e->acceptProposedAction(); } -void HistoryWidget::onPhotoDrop(const QMimeData *data) { - if (!_history) return; - - QStringList files = getMediasFromMime(data); - if (files.isEmpty()) { - if (data->hasImage()) { - QImage image = qvariant_cast(data->imageData()); - if (image.isNull()) return; - - uploadImage(image, PreparePhoto, FileLoadNoForceConfirm, data->text()); - } - return; - } - - uploadFiles(files, PreparePhoto); -} - -void HistoryWidget::onDocumentDrop(const QMimeData *data) { - if (!_history) return; - - if (_peer && !_canSendMessages) return; - - QStringList files = getMediasFromMime(data); - if (files.isEmpty()) return; - - uploadFiles(files, PrepareDocument); -} - -void HistoryWidget::onFilesDrop(const QMimeData *data) { - - if (_peer && !_canSendMessages) return; - - QStringList files = getMediasFromMime(data); - if (files.isEmpty()) { - if (data->hasImage()) { - QImage image = qvariant_cast(data->imageData()); - if (image.isNull()) return; - - uploadImage(image, PrepareAuto, FileLoadNoForceConfirm, data->text()); - } - return; - } - - if (files.size() == 1 && !QFileInfo(files.at(0)).isDir()) { - uploadFile(files.at(0), PrepareAuto); - } -// uploadFiles(files, PrepareAuto); // multiple confirm with "compressed" checkbox -} - void HistoryWidget::onKbToggle(bool manual) { - auto fieldEnabled = canWriteMessage(); + auto fieldEnabled = canWriteMessage() && !_a_show.animating(); if (_kbShown || _kbReplyTo) { _botKeyboardHide->hide(); if (_kbShown) { @@ -6276,7 +6164,7 @@ void HistoryWidget::onKbToggle(bool manual) { } } resizeEvent(0); - if (_botKeyboardHide->isHidden() && canWriteMessage()) { + if (_botKeyboardHide->isHidden() && canWriteMessage() && !_a_show.animating()) { _attachEmoji->show(); } else { _attachEmoji->hide(); @@ -6525,7 +6413,6 @@ void HistoryWidget::moveFieldControls() { _silent->moveToRight(right, buttonsBottom); _fieldBarCancel->moveToRight(0, _field->y() - st::historySendPadding - _fieldBarCancel->height()); - _attachType->moveToLeft(0, _attachToggle->y() - _attachType->height()); _emojiPan->moveBottom(_attachEmoji->y()); auto fullWidthButtonRect = QRect(0, bottom - _botStart->height(), width(), _botStart->height()); @@ -6616,69 +6503,240 @@ void HistoryWidget::updateFieldPlaceholder() { } } -void HistoryWidget::uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm, const QString &source, bool withText) { - if (!_history) return; - +template +bool HistoryWidget::showSendFilesBox(SendFilesBox *box, const QString &insertTextOnCancel, const QString *addedComment, SendCallback callback) { App::wnd()->activateWindow(); - auto task = new FileLoadTask(img, type, FileLoadTo(_peer->id, _silent->checked(), replyToId()), confirm, source); - if (withText) { - _confirmWithTextId = task->fileid(); + + auto withComment = (addedComment != nullptr); + box->setConfirmedCallback(base::lambda_guarded(this, [this, withComment, sendCallback = std_::move(callback)](const QStringList &files, bool compressed, const QString &caption, bool ctrlShiftEnter) { + if (!canWriteMessage()) return; + + auto replyTo = replyToId(); + if (withComment) { + onSend(ctrlShiftEnter, replyTo); + } + sendCallback(files, compressed, caption, replyTo); + })); + + if (withComment) { + auto was = _field->getTextWithTags(); + setFieldText({ *addedComment, TextWithTags::Tags() }); + box->setCancelledCallback(base::lambda_guarded(this, [this, was] { + setFieldText(was); + })); + } else if (!insertTextOnCancel.isEmpty()) { + box->setCancelledCallback(base::lambda_guarded(this, [this, insertTextOnCancel] { + _field->textCursor().insertText(insertTextOnCancel); + })); } - _fileLoader.addTask(task); + + Ui::showLayer(box); + return true; } -void HistoryWidget::uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm, bool withText) { - if (!_history) return; +template +bool HistoryWidget::validateSendingFiles(const SendingFilesLists &lists, Callback callback) { + if (!canWriteMessage()) return false; App::wnd()->activateWindow(); - FileLoadTask *task = new FileLoadTask(file, type, FileLoadTo(_peer->id, _silent->checked(), replyToId()), confirm); - if (withText) { - _confirmWithTextId = task->fileid(); + if (!lists.nonLocalUrls.isEmpty()) { + Ui::showLayer(new InformBox(lng_send_image_non_local(lt_name, lists.nonLocalUrls.front().toDisplayString()))); + } else if (!lists.emptyFiles.isEmpty()) { + Ui::showLayer(new InformBox(lng_send_image_empty(lt_name, lists.emptyFiles.front()))); + } else if (!lists.tooLargeFiles.isEmpty()) { + Ui::showLayer(new InformBox(lng_send_image_too_large(lt_name, lists.tooLargeFiles.front()))); + } else if (!lists.filesToSend.isEmpty()) { + return callback(lists.filesToSend); } - _fileLoader.addTask(task); + return false; } -void HistoryWidget::uploadFiles(const QStringList &files, PrepareMediaType type) { - if (!_history || files.isEmpty()) return; +bool HistoryWidget::confirmSendingFiles(const QList &files, CompressConfirm compressed, const QString *addedComment) { + return confirmSendingFiles(getSendingFilesLists(files), compressed, addedComment); +} - if (files.size() == 1 && !QFileInfo(files.at(0)).isDir()) return uploadFile(files.at(0), type); +bool HistoryWidget::confirmSendingFiles(const QStringList &files, CompressConfirm compressed, const QString *addedComment) { + return confirmSendingFiles(getSendingFilesLists(files), compressed, addedComment); +} + +bool HistoryWidget::confirmSendingFiles(const SendingFilesLists &lists, CompressConfirm compressed, const QString *addedComment) { + return validateSendingFiles(lists, [this, &lists, compressed, addedComment](const QStringList &files) { + auto image = QImage(); + auto insertTextOnCancel = QString(); + auto prepareBox = [this, &files, &lists, compressed, &image] { + if (files.size() > 1) { + return new SendFilesBox(files, lists.allFilesArePhotos ? compressed : CompressConfirm::None); + } + auto filepath = files.front(); + auto animated = false; + image = App::readImage(filepath, nullptr, false, &animated); + return new SendFilesBox(filepath, image, imageCompressConfirm(image, compressed, animated), animated); + }; + auto sendCallback = [this, image](const QStringList &files, bool compressed, const QString &caption, MsgId replyTo) { + auto type = compressed ? SendMediaType::Photo : SendMediaType::File; + uploadFilesAfterConfirmation(files, image, QByteArray(), type, caption); + }; + return showSendFilesBox(prepareBox(), insertTextOnCancel, addedComment, std_::move(sendCallback)); + }); +} + +bool HistoryWidget::confirmSendingFiles(const QImage &image, const QByteArray &content, CompressConfirm compressed, const QString &insertTextOnCancel) { + if (!canWriteMessage() || image.isNull()) return false; App::wnd()->activateWindow(); + auto animated = false; + auto box = new SendFilesBox(QString(), image, imageCompressConfirm(image, compressed), animated); + auto sendCallback = [this, content, image](const QStringList &files, bool compressed, const QString &caption, MsgId replyTo) { + auto type = compressed ? SendMediaType::Photo : SendMediaType::File; + uploadFilesAfterConfirmation(files, image, content, type, caption); + }; + return showSendFilesBox(box, insertTextOnCancel, nullptr, std_::move(sendCallback)); +} - FileLoadTo to(_peer->id, _silent->checked(), replyToId()); +bool HistoryWidget::confirmSendingFiles(const QMimeData *data, CompressConfirm compressed, const QString &insertTextOnCancel) { + if (!canWriteMessage()) { + return false; + } - TasksList tasks; + auto &urls = data->urls(); + if (!urls.isEmpty()) { + for_const (auto &url, urls) { + if (url.isLocalFile()) { + confirmSendingFiles(urls, compressed); + return true; + } + } + } + if (data->hasImage()) { + auto image = qvariant_cast(data->imageData()); + if (!image.isNull()) { + confirmSendingFiles(image, QByteArray(), compressed, insertTextOnCancel); + return true; + } + } + return false; +} + +bool HistoryWidget::confirmShareContact(const QString &phone, const QString &fname, const QString &lname, const QString *addedComment) { + if (!canWriteMessage()) return false; + + auto box = new SendFilesBox(phone, fname, lname); + auto sendCallback = [this, phone, fname, lname](const QStringList &files, bool compressed, const QString &caption, MsgId replyTo) { + shareContact(_peer->id, phone, fname, lname, replyTo); + }; + auto insertTextOnCancel = QString(); + return showSendFilesBox(box, insertTextOnCancel, addedComment, std_::move(sendCallback)); +} + +HistoryWidget::SendingFilesLists HistoryWidget::getSendingFilesLists(const QList &files) { + auto result = SendingFilesLists(); + for_const (auto &url, files) { + if (!url.isLocalFile()) { + result.nonLocalUrls.push_back(url); + } else { + auto filepath = Platform::FileDialog::UrlToLocal(url); + getSendingLocalFileInfo(result, filepath); + } + } + return result; +} + +HistoryWidget::SendingFilesLists HistoryWidget::getSendingFilesLists(const QStringList &files) { + auto result = SendingFilesLists(); + for_const (auto &filepath, files) { + getSendingLocalFileInfo(result, filepath); + } + return result; +} + +void HistoryWidget::getSendingLocalFileInfo(SendingFilesLists &result, const QString &filepath) { + auto hasPhotoExtension = [](const QString &filepath) { + for_const (auto extension, cPhotoExtensions()) { + if (filepath.right(extension.size()).compare(extension, Qt::CaseInsensitive) == 0) { + return true; + } + } + return false; + }; + auto fileinfo = QFileInfo(filepath); + if (fileinfo.isDir()) { + result.directories.push_back(filepath); + } else { + auto filesize = fileinfo.size(); + if (filesize <= 0) { + result.emptyFiles.push_back(filepath); + } else if (filesize > App::kFileSizeLimit) { + result.tooLargeFiles.push_back(filepath); + } else { + result.filesToSend.push_back(filepath); + if (result.allFilesArePhotos) { + if (filesize > App::kImageSizeLimit || !hasPhotoExtension(filepath)) { + result.allFilesArePhotos = false; + } + } + } + } +} + +CompressConfirm HistoryWidget::imageCompressConfirm(const QImage &image, CompressConfirm compressed, bool animated) { + if (animated || image.isNull()) { + return CompressConfirm::None; + } + auto imageWidth = image.width(); + auto imageHeight = image.height(); + if (imageWidth >= 20 * imageHeight || imageHeight >= 20 * imageWidth) { + return CompressConfirm::None; + } + return compressed; +} + +void HistoryWidget::uploadFiles(const QStringList &files, SendMediaType type) { + if (!canWriteMessage()) return; + + auto caption = QString(); + uploadFilesAfterConfirmation(files, QImage(), QByteArray(), type, caption); +} + +void HistoryWidget::uploadFilesAfterConfirmation(const QStringList &files, const QImage &image, const QByteArray &content, SendMediaType type, QString caption) { + t_assert(canWriteMessage()); + + auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); + if (files.size() > 1 && !caption.isEmpty()) { + MainWidget::MessageToSend message; + message.history = _history; + message.textWithTags = { caption, TextWithTags::Tags() }; + message.replyTo = to.replyTo; + message.silent = to.silent; + message.clearDraft = false; + App::main()->sendMessage(message); + caption = QString(); + } + auto tasks = TasksList(); tasks.reserve(files.size()); - for (int32 i = 0, l = files.size(); i < l; ++i) { - tasks.push_back(TaskPtr(new FileLoadTask(files.at(i), type, to, FileLoadNeverConfirm))); + for_const (auto &filepath, files) { + if (filepath.isEmpty() && (!image.isNull() || !content.isNull())) { + tasks.push_back(MakeShared(content, image, type, to, caption)); + } else { + tasks.push_back(MakeShared(filepath, type, to, caption)); + } } _fileLoader.addTasks(tasks); cancelReplyAfterMediaSend(lastForceReplyReplied()); } -void HistoryWidget::uploadFileContent(const QByteArray &fileContent, PrepareMediaType type) { - if (!_history) return; +void HistoryWidget::uploadFile(const QByteArray &fileContent, SendMediaType type) { + if (!canWriteMessage()) return; + + auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); + auto caption = QString(); + _fileLoader.addTask(MakeShared(fileContent, type, to, caption)); - App::wnd()->activateWindow(); - _fileLoader.addTask(new FileLoadTask(fileContent, type, FileLoadTo(_peer->id, _silent->checked(), replyToId()))); cancelReplyAfterMediaSend(lastForceReplyReplied()); } -void HistoryWidget::shareContactWithConfirm(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText) { - if (!_history) return; - - App::wnd()->activateWindow(); - _confirmWithTextId = 0xFFFFFFFFFFFFFFFFL; - Ui::showLayer(new PhotoSendBox(phone, fname, lname, replyTo)); -} - -void HistoryWidget::confirmSendFile(const FileLoadResultPtr &file, bool ctrlShiftEnter) { +void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) { bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(peerToChannel(file->to.peer), file->to.replyTo)); - if (_confirmWithTextId && _confirmWithTextId == file->id) { - onSend(ctrlShiftEnter, file->to.replyTo); - _confirmWithTextId = 0; - } FullMsgId newId(peerToChannel(file->to.peer), clientMsgId()); @@ -6711,11 +6769,11 @@ void HistoryWidget::confirmSendFile(const FileLoadResultPtr &file, bool ctrlShif if (silentPost) { flags |= MTPDmessage::Flag::f_silent; } - if (file->type == PreparePhoto) { + if (file->type == SendMediaType::Photo) { h->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(newId.msg), MTP_int(showFromName ? MTP::authedId() : 0), peerToMTP(file->to.peer), MTPnullFwdHeader, MTPint(), MTP_int(file->to.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(file->photo, MTP_string(file->caption)), MTPnullMarkup, MTPnullEntities, MTP_int(1), MTPint()), NewMessageUnread); - } else if (file->type == PrepareDocument) { + } else if (file->type == SendMediaType::File) { h->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(newId.msg), MTP_int(showFromName ? MTP::authedId() : 0), peerToMTP(file->to.peer), MTPnullFwdHeader, MTPint(), MTP_int(file->to.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(file->document, MTP_string(file->caption)), MTPnullMarkup, MTPnullEntities, MTP_int(1), MTPint()), NewMessageUnread); - } else if (file->type == PrepareAudio) { + } else if (file->type == SendMediaType::Audio) { if (!h->peer->isChannel()) { flags |= MTPDmessage::Flag::f_media_unread; } @@ -6731,34 +6789,6 @@ void HistoryWidget::confirmSendFile(const FileLoadResultPtr &file, bool ctrlShif cancelReplyAfterMediaSend(lastKeyboardUsed); } -void HistoryWidget::cancelSendFile(const FileLoadResultPtr &file) { - if (_confirmWithTextId && file->id == _confirmWithTextId) { - clearFieldText(); - _confirmWithTextId = 0; - } - if (!file->originalText.isEmpty()) { - _field->textCursor().insertText(file->originalText); - } -} - -void HistoryWidget::confirmShareContact(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool ctrlShiftEnter) { - if (!_peer) return; - - PeerId shareToId = _peer->id; - if (_confirmWithTextId == 0xFFFFFFFFFFFFFFFFL) { - onSend(ctrlShiftEnter, replyTo); - _confirmWithTextId = 0; - } - shareContact(shareToId, phone, fname, lname, replyTo); -} - -void HistoryWidget::cancelShareContact() { - if (_confirmWithTextId == 0xFFFFFFFFFFFFFFFFL) { - clearFieldText(); - _confirmWithTextId = 0; - } -} - void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, bool silent, const MTPInputFile &file) { if (!MTP::authedId()) return; HistoryItem *item = App::histItemById(newId); @@ -7688,7 +7718,6 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { if (_membersDropdown) { _membersDropdown->raise(); } - _attachType->raise(); _emojiPan->raise(); _attachDragDocument->raise(); _attachDragPhoto->raise(); @@ -8868,42 +8897,6 @@ void HistoryWidget::destroyData() { showHistory(0, 0); } -QStringList HistoryWidget::getMediasFromMime(const QMimeData *d) { - QString uriListFormat(qsl("text/uri-list")); - QStringList photoExtensions(cPhotoExtensions()), files; - if (!d->hasFormat(uriListFormat)) return QStringList(); - - const QList &urls(d->urls()); - if (urls.isEmpty()) return QStringList(); - - files.reserve(urls.size()); - for (QList::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) { - if (!i->isLocalFile()) return QStringList(); - - auto file = psConvertFileUrl(*i); - - QFileInfo info(file); - uint64 s = info.size(); - if (s >= MaxUploadDocumentSize) { - if (s >= MaxUploadPhotoSize) { - continue; - } else { - bool foundGoodExtension = false; - for (QStringList::const_iterator j = photoExtensions.cbegin(), end = photoExtensions.cend(); j != end; ++j) { - if (file.right(j->size()).toLower() == (*j).toLower()) { - foundGoodExtension = true; - } - } - if (!foundGoodExtension) { - continue; - } - } - } - files.push_back(file); - } - return files; -} - QPoint HistoryWidget::clampMousePosition(QPoint point) { if (point.x() < 0) { point.setX(0); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 53b4b823d6..a25281118c 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "localimageloader.h" +#include "ui/filedialog.h" #include "ui/effects/rect_shadow.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/input_fields.h" @@ -53,6 +54,7 @@ class RoundButton; class DragArea; class EmojiPan; class SilentToggle; +class SendFilesBox; class HistoryWidget; class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber { @@ -582,16 +584,16 @@ public: void updateFieldPlaceholder(); void updateStickersByEmoji(); - void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false); - void uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, bool withText = false); // with confirmation - void uploadFiles(const QStringList &files, PrepareMediaType type); - void uploadFileContent(const QByteArray &fileContent, PrepareMediaType type); - void shareContactWithConfirm(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText = false); + bool confirmSendingFiles(const QList &files, CompressConfirm compressed = CompressConfirm::Auto, const QString *addedComment = nullptr); + bool confirmSendingFiles(const QStringList &files, CompressConfirm compressed = CompressConfirm::Auto, const QString *addedComment = nullptr); + bool confirmSendingFiles(const QImage &image, const QByteArray &content, CompressConfirm compressed = CompressConfirm::Auto, const QString &insertTextOnCancel = QString()); + bool confirmSendingFiles(const QMimeData *data, CompressConfirm compressed = CompressConfirm::Auto, const QString &insertTextOnCancel = QString()); + bool confirmShareContact(const QString &phone, const QString &fname, const QString &lname, const QString *addedComment = nullptr); - void confirmSendFile(const FileLoadResultPtr &file, bool ctrlShiftEnter); - void cancelSendFile(const FileLoadResultPtr &file); - void confirmShareContact(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool ctrlShiftEnter); - void cancelShareContact(); + void uploadFile(const QByteArray &fileContent, SendMediaType type); + void uploadFiles(const QStringList &files, SendMediaType type); + + void sendFileConfirmed(const FileLoadResultPtr &file); void updateControlsVisibility(); void updateControlsGeometry(); @@ -599,7 +601,6 @@ public: void updateOnlineDisplayTimer(); void onShareContact(const PeerId &peer, UserData *contact); - void onSendPaths(const PeerId &peer); void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId = 0); @@ -788,12 +789,6 @@ public slots: void onMuteUnmute(); void onBroadcastSilentChange(); - void onPhotoSelect(); - void onDocumentSelect(); - void onPhotoDrop(const QMimeData *data); - void onDocumentDrop(const QMimeData *data); - void onFilesDrop(const QMimeData *data); - void onKbToggle(bool manual = true); void onCmdStart(); @@ -854,6 +849,29 @@ private slots: void updateField(); private: + void chooseAttach(); + void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); + struct SendingFilesLists { + QList nonLocalUrls; + QStringList directories; + QStringList emptyFiles; + QStringList tooLargeFiles; + QStringList filesToSend; + bool allFilesArePhotos = true; + }; + SendingFilesLists getSendingFilesLists(const QList &files); + SendingFilesLists getSendingFilesLists(const QStringList &files); + void getSendingLocalFileInfo(SendingFilesLists &result, const QString &filepath); + bool confirmSendingFiles(const SendingFilesLists &lists, CompressConfirm compressed = CompressConfirm::Auto, const QString *addedComment = nullptr); + template + bool validateSendingFiles(const SendingFilesLists &lists, Callback callback); + template + bool showSendFilesBox(SendFilesBox *box, const QString &insertTextOnCancel, const QString *addedComment, SendCallback callback); + CompressConfirm imageCompressConfirm(const QImage &image, CompressConfirm compressed, bool animated = false); + + // If an empty filepath is found we upload (possible) "image" with (possible) "content". + void uploadFilesAfterConfirmation(const QStringList &files, const QImage &image, const QByteArray &content, SendMediaType type, QString caption); + void itemRemoved(HistoryItem *item); // Updates position of controls around the message field, @@ -1032,8 +1050,6 @@ private: setFieldText(TextWithTags(), events, undoHistoryAction); } - QStringList getMediasFromMime(const QMimeData *d); - void updateDragAreas(); // when scroll position or scroll area size changed this method @@ -1119,6 +1135,8 @@ private: anim::fvalue a_recordCancelActive; int32 _recordCancelWidth; + FileDialog::QueryId _attachFilesQueryId = 0; + bool kbWasHidden() const; bool _kbShown = false; @@ -1129,7 +1147,6 @@ private: ChildWidget _membersDropdown = { nullptr }; QTimer _membersDropdownShowTimer; - ChildWidget _attachType; ChildWidget _emojiPan; DragState _attachDrag = DragStateNone; ChildWidget _attachDragDocument, _attachDragPhoto; @@ -1142,8 +1159,6 @@ private: int64 _serviceImageCacheSize = 0; QString _confirmSource; - uint64 _confirmWithTextId = 0; - QString _titlePeerText; bool _titlePeerTextOnline = false; int _titlePeerTextWidth = 0; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 5addf4e2b6..44ff47b6bf 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -282,7 +282,7 @@ void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { if (!document->thumb->isNull()) { if (document->thumb->loaded()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { - _thumb = document->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = document->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { document->thumb->load(); @@ -293,7 +293,7 @@ void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { if (!thumb->isNull()) { if (thumb->loaded()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { - _thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { thumb->load(); @@ -529,13 +529,13 @@ void Photo::prepareThumb(int32 width, int32 height, const QSize &frame) const { if (PhotoData *photo = getShownPhoto()) { if (photo->medium->loaded()) { if (!_thumbLoaded || _thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { - _thumb = photo->medium->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = photo->medium->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); } _thumbLoaded = true; } else { if (photo->thumb->loaded()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { - _thumb = photo->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = photo->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } photo->medium->load(); @@ -544,7 +544,7 @@ void Photo::prepareThumb(int32 width, int32 height, const QSize &frame) const { ImagePtr thumb = getResultThumb(); if (thumb->loaded()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { - _thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { thumb->load(); @@ -654,7 +654,7 @@ void Video::prepareThumb(int32 width, int32 height) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { thumb->load(); @@ -985,7 +985,7 @@ void Contact::prepareThumb(int width, int height) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { thumb->load(); @@ -1132,7 +1132,7 @@ void Article::prepareThumb(int width, int height) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { thumb->load(); @@ -1312,7 +1312,7 @@ void Game::prepareThumb(int width, int height) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixOption::Smooth, width, height); + _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); } } else { thumb->load(); diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index de2d361288..3ab0da955f 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -45,7 +45,7 @@ SignupWidget::SignupWidget(QWidget *parent, Widget::Data *data) : Step(parent, d _photo->setClickedCallback([this] { auto imgExtensions = cImgExtensions(); auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter(); - _readPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter); + _readPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_image), filter); }); subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) { notifyFileQueryUpdated(update); diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index 0cb6d72320..b76d568873 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" #include "media/media_audio.h" -#include "boxes/photosendbox.h" +#include "boxes/send_files_box.h" #include "media/media_clip_reader.h" #include "mainwidget.h" #include "mainwindow.h" @@ -172,39 +172,34 @@ void TaskQueueWorker::onTaskAdded() { _inTaskAdded = false; } -FileLoadTask::FileLoadTask(const QString &filepath, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm) : _id(rand_value()) +FileLoadTask::FileLoadTask(const QString &filepath, SendMediaType type, const FileLoadTo &to, const QString &caption) : _id(rand_value()) , _to(to) , _filepath(filepath) , _type(type) -, _confirm(confirm) { +, _caption(caption) { } -FileLoadTask::FileLoadTask(const QByteArray &content, PrepareMediaType type, const FileLoadTo &to) : _id(rand_value()) +FileLoadTask::FileLoadTask(const QByteArray &content, const QImage &image, SendMediaType type, const FileLoadTo &to, const QString &caption) : _id(rand_value()) , _to(to) , _content(content) -, _type(type) { -} - -FileLoadTask::FileLoadTask(const QImage &image, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm, const QString &originalText) : _id(rand_value()) -, _to(to) , _image(image) , _type(type) -, _confirm(confirm) -, _originalText(originalText) { +, _caption(caption) { } -FileLoadTask::FileLoadTask(const QByteArray &voice, int32 duration, const VoiceWaveform &waveform, const FileLoadTo &to) : _id(rand_value()) +FileLoadTask::FileLoadTask(const QByteArray &voice, int32 duration, const VoiceWaveform &waveform, const FileLoadTo &to, const QString &caption) : _id(rand_value()) , _to(to) , _content(voice) , _duration(duration) , _waveform(waveform) -, _type(PrepareAudio) { +, _type(SendMediaType::Audio) +, _caption(caption) { } void FileLoadTask::process() { const QString stickerMime = qsl("image/webp"); - _result = FileLoadResultPtr(new FileLoadResult(_id, _to, _originalText)); + _result = MakeShared(_id, _to, _caption); QString filename, filemime; qint64 filesize = 0; @@ -214,8 +209,11 @@ void FileLoadTask::process() { QString thumbname = "thumb.jpg"; QByteArray thumbdata; - bool animated = false, song = false, gif = false, voice = (_type == PrepareAudio); - QImage fullimage = _image; + auto animated = false; + auto song = false; + auto gif = false; + auto voice = (_type == SendMediaType::Audio); + auto fullimage = base::take(_image); if (!_filepath.isEmpty()) { QFileInfo info(_filepath); @@ -226,24 +224,23 @@ void FileLoadTask::process() { filesize = info.size(); filemime = mimeTypeForFile(info).name(); filename = info.fileName(); - if (filesize <= MaxUploadPhotoSize && !voice) { - bool opaque = (filemime != stickerMime); - fullimage = App::readImage(_filepath, 0, opaque, &animated); - } + auto opaque = (filemime != stickerMime); + fullimage = App::readImage(_filepath, 0, opaque, &animated); } else if (!_content.isEmpty()) { filesize = _content.size(); if (voice) { filename = filedialogDefaultName(qsl("audio"), qsl(".ogg"), QString(), true); filemime = "audio/ogg"; } else { - MimeType mimeType = mimeTypeForData(_content); + auto mimeType = mimeTypeForData(_content); filemime = mimeType.name(); - if (filesize <= MaxUploadPhotoSize && !voice) { - bool opaque = (filemime != stickerMime); - fullimage = App::readImage(_content, 0, opaque, &animated); + if (filemime != stickerMime) { + fullimage = Images::prepareOpaque(std_::move(fullimage)); } if (filemime == "image/jpeg") { - filename = filedialogDefaultName(qsl("image"), qsl(".jpg"), QString(), true); + filename = filedialogDefaultName(qsl("photo"), qsl(".jpg"), QString(), true); + } else if (filemime == "image/png") { + filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); } else { QString ext; QStringList patterns = mimeType.globPatterns(); @@ -253,9 +250,7 @@ void FileLoadTask::process() { filename = filedialogDefaultName(qsl("file"), ext, QString(), true); } } - } else if (!_image.isNull()) { - _image = QImage(); - + } else if (!fullimage.isNull()) { filemime = mimeTypeForName("image/png").name(); filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); { @@ -264,18 +259,11 @@ void FileLoadTask::process() { } filesize = _content.size(); - if (fullimage.hasAlphaChannel()) { - QImage solid(fullimage.width(), fullimage.height(), QImage::Format_ARGB32_Premultiplied); - solid.fill(st::imageBgTransparent->c); - { - QPainter(&solid).drawImage(0, 0, fullimage); - } - fullimage = solid; - } + fullimage = Images::prepareOpaque(std_::move(fullimage)); } _result->filesize = (int32)qMin(filesize, qint64(INT_MAX)); - if (!filesize || filesize > MaxUploadDocumentSize) { + if (!filesize || filesize > App::kFileSizeLimit) { return; } @@ -359,7 +347,7 @@ void FileLoadTask::process() { if (w < 20 * h && h < 20 * w) { if (animated) { attributes.push_back(MTP_documentAttributeAnimated()); - } else if (_type != PrepareDocument) { + } else if (_type != SendMediaType::File) { auto thumb = (w > 100 || h > 100) ? App::pixmapFromImageInPlace(fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); photoThumbs.insert('s', thumb); photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); @@ -411,7 +399,7 @@ void FileLoadTask::process() { } else { document = MTP_document(MTP_long(_id), MTP_long(0), MTP_int(unixtime()), MTP_string(filemime), MTP_int(filesize), thumbSize, MTP_int(MTP::maindc()), MTP_int(0), MTP_vector(attributes)); if (photo.type() == mtpc_photoEmpty) { - _type = PrepareDocument; + _type = SendMediaType::File; } } @@ -435,29 +423,12 @@ void FileLoadTask::process() { void FileLoadTask::finish() { if (!_result || !_result->filesize) { - if (_result) App::main()->onSendFileCancel(_result); - Ui::showLayer(new InformBox(lang(lng_send_image_empty)), KeepOtherLayers); - return; - } - if (_result->filesize == -1) { // dir - App::main()->onSendFileCancel(_result); + Ui::showLayer(new InformBox(lng_send_image_empty(lt_name, _filepath)), KeepOtherLayers); + } else if (_result->filesize == -1) { // dir Ui::showLayer(new InformBox(lng_send_folder(lt_name, QFileInfo(_filepath).dir().dirName())), KeepOtherLayers); - return; - } - if (_result->filesize > MaxUploadDocumentSize) { - App::main()->onSendFileCancel(_result); - Ui::showLayer(new InformBox(lang(lng_send_image_too_large)), KeepOtherLayers); - return; - } - if (App::main()) { - bool confirm = (_confirm == FileLoadAlwaysConfirm) || (_result->photo.type() != mtpc_photoEmpty && _confirm != FileLoadNeverConfirm); - if (confirm) { - Ui::showLayer(new PhotoSendBox(_result), ShowAfterOtherLayers); - } else { - if (_result->type == PrepareAuto) { - _result->type = (_result->photo.type() != mtpc_photoEmpty) ? PreparePhoto : PrepareDocument; - } - App::main()->onSendFileConfirm(_result, false); - } + } else if (_result->filesize > App::kFileSizeLimit) { + Ui::showLayer(new InformBox(lng_send_image_too_large(lt_name, _filepath)), KeepOtherLayers); + } else if (App::main()) { + App::main()->onSendFileConfirm(_result); } } diff --git a/Telegram/SourceFiles/localimageloader.h b/Telegram/SourceFiles/localimageloader.h index e531d4021d..43fb11062e 100644 --- a/Telegram/SourceFiles/localimageloader.h +++ b/Telegram/SourceFiles/localimageloader.h @@ -20,39 +20,57 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -enum PrepareMediaType { - PrepareAuto, - PreparePhoto, - PrepareAudio, - PrepareVideo, - PrepareDocument, +enum class CompressConfirm { + Auto, + Yes, + No, + None, }; -struct ToPrepareMedia { - ToPrepareMedia(const QString &file, const PeerId &peer, PrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(rand_value()), file(file), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) { +enum class SendMediaType { + Photo, + Audio, + File, +}; + +struct SendMediaPrepare { + SendMediaPrepare(const QString &file, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), file(file), peer(peer), type(type), replyTo(replyTo) { } - ToPrepareMedia(const QImage &img, const PeerId &peer, PrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(rand_value()), img(img), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) { + SendMediaPrepare(const QImage &img, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), img(img), peer(peer), type(type), replyTo(replyTo) { } - ToPrepareMedia(const QByteArray &data, const PeerId &peer, PrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(rand_value()), data(data), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) { + SendMediaPrepare(const QByteArray &data, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), data(data), peer(peer), type(type), replyTo(replyTo) { } - ToPrepareMedia(const QByteArray &data, int32 duration, const PeerId &peer, PrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(rand_value()), data(data), peer(peer), type(t), duration(duration), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) { + SendMediaPrepare(const QByteArray &data, int duration, const PeerId &peer, SendMediaType type, MsgId replyTo) : id(rand_value()), data(data), peer(peer), type(type), duration(duration), replyTo(replyTo) { } PhotoId id; QString file; QImage img; QByteArray data; PeerId peer; - PrepareMediaType type; - int32 duration; - bool ctrlShiftEnter; + SendMediaType type; + int duration = 0; MsgId replyTo; -}; -typedef QList ToPrepareMedias; -typedef QMap UploadFileParts; -struct ReadyLocalMedia { - ReadyLocalMedia(PrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) : - replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) { +}; +using SendMediaPrepareList = QList; + +using UploadFileParts = QMap; +struct SendMediaReady { + SendMediaReady() = default; // temp + SendMediaReady(SendMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, MsgId replyTo) + : replyTo(replyTo) + , type(type) + , file(file) + , filename(filename) + , filesize(filesize) + , data(data) + , thumbExt(thumbExt) + , id(id) + , thumbId(thumbId) + , peer(peer) + , photo(photo) + , document(document) + , photoThumbs(photoThumbs) { if (!jpeg.isEmpty()) { int32 size = jpeg.size(); for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) { @@ -63,7 +81,7 @@ struct ReadyLocalMedia { } } MsgId replyTo; - PrepareMediaType type; + SendMediaType type; QString file, filename; int32 filesize; QByteArray data; @@ -77,58 +95,45 @@ struct ReadyLocalMedia { UploadFileParts parts; QByteArray jpeg_md5; - bool ctrlShiftEnter; QString caption; - ReadyLocalMedia() : type(PrepareAuto) { // temp - } }; class Task { public: - virtual void process() = 0; // is executed in a separate thread virtual void finish() = 0; // is executed in the same as TaskQueue thread - virtual ~Task() { - } + virtual ~Task() = default; TaskId id() const { - return TaskId(this); + return static_cast(const_cast(this)); } }; -typedef QSharedPointer TaskPtr; -typedef QList TasksList; +using TaskPtr = QSharedPointer; +using TasksList = QList; class TaskQueueWorker; class TaskQueue : public QObject { Q_OBJECT public: - TaskQueue(QObject *parent, int32 stopTimeoutMs = 0); // <= 0 - never stop worker TaskId addTask(TaskPtr task); void addTasks(const TasksList &tasks); void cancelTask(TaskId id); // this task finish() won't be called - TaskId addTask(Task *task) { - return addTask(TaskPtr(task)); - } - ~TaskQueue(); signals: - void taskAdded(); public slots: - void onTaskProcessed(); void stop(); private: - friend class TaskQueueWorker; void wakeThread(); @@ -145,21 +150,18 @@ class TaskQueueWorker : public QObject { Q_OBJECT public: - - TaskQueueWorker(TaskQueue *queue) : _queue(queue), _inTaskAdded(false) { + TaskQueueWorker(TaskQueue *queue) : _queue(queue) { } signals: - void taskProcessed(); public slots: - void onTaskAdded(); private: TaskQueue *_queue; - bool _inTaskAdded; + bool _inTaskAdded = false; }; @@ -175,28 +177,26 @@ struct FileLoadTo { }; struct FileLoadResult { - FileLoadResult(const uint64 &id, const FileLoadTo &to, const QString &originalText) : id(id) + FileLoadResult(const uint64 &id, const FileLoadTo &to, const QString &caption) + : id(id) , to(to) - , type(PrepareAuto) - , filesize(0) - , thumbId(0) - , originalText(originalText) { + , caption(caption) { } uint64 id; FileLoadTo to; - PrepareMediaType type; + SendMediaType type; QString filepath; QByteArray content; QString filename; QString filemime; - int32 filesize; + int32 filesize = 0; UploadFileParts fileparts; QByteArray filemd5; int32 partssize; - uint64 thumbId; // id is always file-id of media, thumbId is file-id of thumb ( == id for photos) + uint64 thumbId = 0; // id is always file-id of media, thumbId is file-id of thumb ( == id for photos) QString thumbname; UploadFileParts thumbparts; QByteArray thumbmd5; @@ -208,8 +208,6 @@ struct FileLoadResult { PreparedPhotoThumbs photoThumbs; QString caption; - QString originalText; // when pasted had an image mime save text mime here to insert if image send was cancelled - void setFileData(const QByteArray &filedata) { if (filedata.isEmpty()) { partssize = 0; @@ -235,19 +233,11 @@ struct FileLoadResult { }; typedef QSharedPointer FileLoadResultPtr; -enum FileLoadForceConfirmType { - FileLoadNoForceConfirm, - FileLoadNeverConfirm, - FileLoadAlwaysConfirm, -}; - class FileLoadTask : public Task { public: - - FileLoadTask(const QString &filepath, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm); - FileLoadTask(const QByteArray &content, PrepareMediaType type, const FileLoadTo &to); - FileLoadTask(const QImage &image, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &originalText = QString()); - FileLoadTask(const QByteArray &voice, int32 duration, const VoiceWaveform &waveform, const FileLoadTo &to); + FileLoadTask(const QString &filepath, SendMediaType type, const FileLoadTo &to, const QString &caption); + FileLoadTask(const QByteArray &content, const QImage &image, SendMediaType type, const FileLoadTo &to, const QString &caption); + FileLoadTask(const QByteArray &voice, int32 duration, const VoiceWaveform &waveform, const FileLoadTo &to, const QString &caption); uint64 fileid() const { return _id; @@ -257,17 +247,15 @@ public: void finish(); protected: - uint64 _id; FileLoadTo _to; QString _filepath; - QImage _image; QByteArray _content; + QImage _image; int32 _duration = 0; VoiceWaveform _waveform; - PrepareMediaType _type; - FileLoadForceConfirmType _confirm = FileLoadNoForceConfirm; - QString _originalText; + SendMediaType _type; + QString _caption; FileLoadResultPtr _result; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index c7ccf41e0e..b00f0304bd 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -1234,11 +1234,6 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { qint32 v; stream >> v; if (!_checkStreamStatus(stream)) return false; - - switch (v) { - case dbidaPhoto: cSetDefaultAttach(dbidaPhoto); break; - default: cSetDefaultAttach(dbidaDocument); break; - } } break; case dbiNotifyView: { @@ -1631,7 +1626,6 @@ void _writeUserSettings() { data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0); data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); data.stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0); - data.stream << quint32(dbiDefaultAttach) << qint32(cDefaultAttach()); data.stream << quint32(dbiSoundNotify) << qint32(Global::SoundNotify()); data.stream << quint32(dbiIncludeMuted) << qint32(Global::IncludeMuted()); data.stream << quint32(dbiShowingSavedGifs) << qint32(cShowingSavedGifs()); @@ -2738,7 +2732,7 @@ TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader) { if (j == _imagesMap.cend() || !_localLoader) { return 0; } - return _localLoader->addTask(new ImageLoadTask(j->first, location, loader)); + return _localLoader->addTask(MakeShared(j->first, location, loader)); } int32 hasImages() { @@ -2797,7 +2791,7 @@ TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader) if (j == _stickerImagesMap.cend() || !_localLoader) { return 0; } - return _localLoader->addTask(new StickerImageLoadTask(j->first, location, loader)); + return _localLoader->addTask(MakeShared(j->first, location, loader)); } bool willStickerImageLoad(const StorageKey &location) { @@ -2871,7 +2865,7 @@ TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader) { if (j == _audiosMap.cend() || !_localLoader) { return 0; } - return _localLoader->addTask(new AudioLoadTask(j->first, location, loader)); + return _localLoader->addTask(MakeShared(j->first, location, loader)); } bool copyAudio(const StorageKey &oldLocation, const StorageKey &newLocation) { @@ -2986,7 +2980,7 @@ TaskId startWebFileLoad(const QString &url, webFileLoader *loader) { if (j == _webFilesMap.cend() || !_localLoader) { return 0; } - return _localLoader->addTask(new WebFileLoadTask(j->first, url, loader)); + return _localLoader->addTask(MakeShared(j->first, url, loader)); } int32 hasWebFiles() { @@ -3062,7 +3056,7 @@ void countVoiceWaveform(DocumentData *document) { if (_localLoader) { voice->waveform.resize(1 + sizeof(TaskId)); voice->waveform[0] = -1; // counting - TaskId taskId = _localLoader->addTask(new CountWaveformTask(document)); + TaskId taskId = _localLoader->addTask(MakeShared(document)); memcpy(voice->waveform.data() + 1, &taskId, sizeof(taskId)); } } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 994738a65d..dc7a499f0d 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -450,8 +450,9 @@ void MainWidget::onShareContact(const PeerId &peer, UserData *contact) { _history->onShareContact(peer, contact); } -void MainWidget::onSendPaths(const PeerId &peer) { - _history->onSendPaths(peer); +bool MainWidget::onSendPaths(const PeerId &peer) { + Ui::showPeerHistory(peer, ShowAtTheEndMsgId); + return _history->confirmSendingFiles(cSendPaths()); } void MainWidget::onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data) { @@ -463,7 +464,7 @@ void MainWidget::onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data) onForward(peer, ForwardPressedMessage); } else { Ui::showPeerHistory(peer, ShowAtTheEndMsgId); - _history->onFilesDrop(data); + _history->confirmSendingFiles(data); } } @@ -1779,20 +1780,8 @@ void MainWidget::updateOnlineDisplay() { _history->updateOnlineDisplay(); } -void MainWidget::onSendFileConfirm(const FileLoadResultPtr &file, bool ctrlShiftEnter) { - _history->confirmSendFile(file, ctrlShiftEnter); -} - -void MainWidget::onSendFileCancel(const FileLoadResultPtr &file) { - _history->cancelSendFile(file); -} - -void MainWidget::onShareContactConfirm(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool ctrlShiftEnter) { - _history->confirmShareContact(phone, fname, lname, replyTo, ctrlShiftEnter); -} - -void MainWidget::onShareContactCancel() { - _history->cancelShareContact(); +void MainWidget::onSendFileConfirm(const FileLoadResultPtr &file) { + _history->sendFileConfirmed(file); } bool MainWidget::onSendSticker(DocumentData *document) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 055586f81c..0f8f0a29fb 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -212,10 +212,7 @@ public: QRect historyRect() const; QPixmap grabForShowAnimation(const Window::SectionSlideParams ¶ms); - void onSendFileConfirm(const FileLoadResultPtr &file, bool ctrlShiftEnter); - void onSendFileCancel(const FileLoadResultPtr &file); - void onShareContactConfirm(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool ctrlShiftEnter); - void onShareContactCancel(); + void onSendFileConfirm(const FileLoadResultPtr &file); bool onSendSticker(DocumentData *sticker); void destroyData(); @@ -243,7 +240,7 @@ public: bool onShareUrl(const PeerId &peer, const QString &url, const QString &text); bool onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery); void onShareContact(const PeerId &peer, UserData *contact); - void onSendPaths(const PeerId &peer); + bool onSendPaths(const PeerId &peer); void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data); bool selectingPeer(bool withConfirm = false); bool selectingPeerForInlineSwitch(); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 7ea3b6d6c2..1df7014bee 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -413,6 +413,9 @@ void MainWindow::showMainMenu() { void MainWindow::ui_hideSettingsAndLayer(ShowLayerOptions options) { if (_layerBg) { _layerBg->hideAll(); + if (options.testFlag(ForceFastShowLayer)) { + _layerBg.destroyDelayed(); + } } } @@ -1412,7 +1415,7 @@ QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, void MainWindow::sendPaths() { if (App::passcoded()) return; hideMediaview(); - Ui::hideSettingsAndLayer(); + Ui::hideSettingsAndLayer(true); if (_main) { _main->activate(); } diff --git a/Telegram/SourceFiles/media/media_clip_reader.cpp b/Telegram/SourceFiles/media/media_clip_reader.cpp index a458358d95..b0c2ff511d 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/media_clip_reader.cpp @@ -76,7 +76,7 @@ QPixmap _prepareFrame(const FrameRequest &request, const QImage &original, bool } } if (request.radius != ImageRoundRadius::None) { - imageRound(cache, request.radius, request.corners); + Images::prepareRound(cache, request.radius, request.corners); } return QPixmap::fromImage(cache, Qt::ColorOnly); } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index a58848b053..c5d979c949 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -491,7 +491,7 @@ void MediaView::step_radial(uint64 ms, bool timer) { if (timer && (wasAnimating || _radial.animating())) { update(radialRect()); } - if (_doc && _doc->loaded() && _doc->size < MediaViewImageSizeLimit && (!_radial.animating() || _doc->isAnimation() || _doc->isVideo())) { + if (_doc && _doc->loaded() && _doc->size < App::kImageSizeLimit && (!_radial.animating() || _doc->isAnimation() || _doc->isVideo())) { if (_doc->isVideo()) { _autoplayVideoDocument = _doc; } @@ -1329,10 +1329,10 @@ void MediaView::initAnimation() { } else if (_doc->dimensions.width() && _doc->dimensions.height()) { int w = _doc->dimensions.width(); int h = _doc->dimensions.height(); - _current = _doc->thumb->pixNoCache(w, h, ImagePixOption::Smooth | ImagePixOption::Blurred, w / cIntRetinaFactor(), h / cIntRetinaFactor()); + _current = _doc->thumb->pixNoCache(w, h, Images::Option::Smooth | Images::Option::Blurred, w / cIntRetinaFactor(), h / cIntRetinaFactor()); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } else { - _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixOption::Smooth | ImagePixOption::Blurred, st::mediaviewFileIconSize, st::mediaviewFileIconSize); + _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), Images::Option::Smooth | Images::Option::Blurred, st::mediaviewFileIconSize, st::mediaviewFileIconSize); } } @@ -1345,10 +1345,10 @@ void MediaView::createClipReader() { if (_doc->dimensions.width() && _doc->dimensions.height()) { int w = _doc->dimensions.width(); int h = _doc->dimensions.height(); - _current = _doc->thumb->pixNoCache(w, h, ImagePixOption::Smooth | ImagePixOption::Blurred, w / cIntRetinaFactor(), h / cIntRetinaFactor()); + _current = _doc->thumb->pixNoCache(w, h, Images::Option::Smooth | Images::Option::Blurred, w / cIntRetinaFactor(), h / cIntRetinaFactor()); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } else { - _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixOption::Smooth | ImagePixOption::Blurred, st::mediaviewFileIconSize, st::mediaviewFileIconSize); + _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), Images::Option::Smooth | Images::Option::Blurred, st::mediaviewFileIconSize, st::mediaviewFileIconSize); } auto mode = _doc->isVideo() ? Media::Clip::Reader::Mode::Video : Media::Clip::Reader::Mode::Gif; _gif = std_::make_unique(_doc->location(), _doc->data(), [this](Media::Clip::Notification notification) { @@ -1543,17 +1543,17 @@ void MediaView::paintEvent(QPaintEvent *e) { int32 w = _width * cIntRetinaFactor(); if (_full <= 0 && _photo->loaded()) { int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); - _current = _photo->full->pixNoCache(w, h, ImagePixOption::Smooth); + _current = _photo->full->pixNoCache(w, h, Images::Option::Smooth); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); _full = 1; } else if (_full < 0 && _photo->medium->loaded()) { int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); - _current = _photo->medium->pixNoCache(w, h, ImagePixOption::Smooth | ImagePixOption::Blurred); + _current = _photo->medium->pixNoCache(w, h, Images::Option::Smooth | Images::Option::Blurred); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); _full = 0; } else if (_current.isNull() && _photo->thumb->loaded()) { int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); - _current = _photo->thumb->pixNoCache(w, h, ImagePixOption::Smooth | ImagePixOption::Blurred); + _current = _photo->thumb->pixNoCache(w, h, Images::Option::Smooth | Images::Option::Blurred); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } else if (_current.isNull()) { _current = _photo->thumb->pix(); diff --git a/Telegram/SourceFiles/mtproto/file_download.h b/Telegram/SourceFiles/mtproto/file_download.h index 7623842883..3aff211833 100644 --- a/Telegram/SourceFiles/mtproto/file_download.h +++ b/Telegram/SourceFiles/mtproto/file_download.h @@ -94,7 +94,7 @@ enum LocalLoadStatus { LocalFailed, }; -typedef void *TaskId; // no interface, just id +using TaskId = void*; // no interface, just id enum LoadFromCloudSetting { LoadFromCloudOrLocal, diff --git a/Telegram/SourceFiles/mtproto/rpc_sender.h b/Telegram/SourceFiles/mtproto/rpc_sender.h index 0111d13de6..1c09c89c6d 100644 --- a/Telegram/SourceFiles/mtproto/rpc_sender.h +++ b/Telegram/SourceFiles/mtproto/rpc_sender.h @@ -827,17 +827,6 @@ protected: typedef void (*MTPStateChangedHandler)(int32 dcId, int32 state); typedef void(*MTPSessionResetHandler)(int32 dcId); -template -struct LambdaUniqueHelper; - -template -struct LambdaUniqueHelper { - using UniqueType = base::lambda; -}; - -template -using LambdaGetUnique = typename LambdaUniqueHelper::UniqueType; - template class RPCHandlerImplementation : public Base { protected: @@ -948,7 +937,7 @@ inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda::value>> RPCDoneHandlerPtr rpcDone(Lambda &&lambda) { - return rpcDone_lambda_wrap_helper(LambdaGetUnique(std_::move(lambda))); + return rpcDone_lambda_wrap_helper(base::lambda_type(std_::move(lambda))); } template @@ -1008,5 +997,5 @@ inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda::value>> RPCFailHandlerPtr rpcFail(Lambda &&lambda) { - return rpcFail_lambda_wrap_helper(LambdaGetUnique(std_::move(lambda))); + return rpcFail_lambda_wrap_helper(base::lambda_type(std_::move(lambda))); } diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 72256c1de5..23302cb0a8 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -240,9 +240,9 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const int32 size = _width * cIntRetinaFactor(); if (_goodLoaded || _data->thumb->loaded()) { - QImage img = (_data->loaded() ? _data->full : (_data->medium->loaded() ? _data->medium : _data->thumb))->pix().toImage(); + auto img = (_data->loaded() ? _data->full : (_data->medium->loaded() ? _data->medium : _data->thumb))->pix().toImage(); if (!_goodLoaded) { - img = imageBlur(img); + img = Images::prepareBlur(img); } if (img.width() == img.height()) { if (img.width() != size) { @@ -337,7 +337,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const if (_thumbLoaded && !_data->thumb->isNull()) { int32 size = _width * cIntRetinaFactor(); - QImage img = imageBlur(_data->thumb->pix().toImage()); + auto img = Images::prepareBlur(_data->thumb->pix().toImage()); if (img.width() == img.height()) { if (img.width() != size) { img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); @@ -797,8 +797,8 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con if (_data->thumb->loaded()) { if (_thumb.isNull() || loaded != _thumbForLoaded) { _thumbForLoaded = loaded; - auto options = ImagePixOption::Smooth | ImagePixOption::None; - if (!_thumbForLoaded) options |= ImagePixOption::Blurred; + auto options = Images::Option::Smooth | Images::Option::None; + if (!_thumbForLoaded) options |= Images::Option::Blurred; _thumb = _data->thumb->pixNoCache(_thumbw * cIntRetinaFactor(), 0, options, _st.fileThumbSize, _st.fileThumbSize); } p.drawPixmap(rthumb.topLeft(), _thumb); diff --git a/Telegram/SourceFiles/platform/linux/file_dialog_linux.h b/Telegram/SourceFiles/platform/linux/file_dialog_linux.h index 3cc2a935de..6892681e5b 100644 --- a/Telegram/SourceFiles/platform/linux/file_dialog_linux.h +++ b/Telegram/SourceFiles/platform/linux/file_dialog_linux.h @@ -36,6 +36,10 @@ bool Supported(); bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile); +inline QString UrlToLocal(const QUrl &url) { + return url.toLocalFile(); +} + namespace internal { // This is a patched copy of qgtk2 theme plugin. diff --git a/Telegram/SourceFiles/platform/mac/file_dialog_mac.h b/Telegram/SourceFiles/platform/mac/file_dialog_mac.h new file mode 100644 index 0000000000..0718f08ffb --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/file_dialog_mac.h @@ -0,0 +1,37 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Platform { +namespace FileDialog { + +inline bool Supported() { + return false; +} + +inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) { + return false; +} + +QString UrlToLocal(const QUrl &url); + +} // namespace FileDialog +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/file_dialog_mac.mm b/Telegram/SourceFiles/platform/mac/file_dialog_mac.mm new file mode 100644 index 0000000000..827a181070 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/file_dialog_mac.mm @@ -0,0 +1,44 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/mac/file_dialog_mac.h" + +#include "platform/mac/mac_utilities.h" + +#include +#include + +namespace Platform { +namespace FileDialog { + +QString UrlToLocal(const QUrl &url) { + auto result = url.toLocalFile(); + if (result.startsWith(qsl("/.file/id="))) { + NSString *nsurl = [[[NSURL URLWithString: [NSString stringWithUTF8String: (qsl("file://") + result).toUtf8().constData()]] filePathURL] path]; + if (!nsurl) return QString(); + + return NS2QString(nsurl); + } + return result; +} + +} // namespace FileDialog +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/platform_file_dialog.h b/Telegram/SourceFiles/platform/platform_file_dialog.h index 1a1a6c5541..e6f5e779c3 100644 --- a/Telegram/SourceFiles/platform/platform_file_dialog.h +++ b/Telegram/SourceFiles/platform/platform_file_dialog.h @@ -22,17 +22,28 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" -#if defined Q_OS_LINUX +#ifdef Q_OS_MAC +#include "platform/mac/file_dialog_mac.h" +#elif defined Q_OS_LINUX // Q_OS_MAC #include "platform/linux/file_dialog_linux.h" -#else // Q_OS_LINUX +#elif defined Q_OS_WINRT || defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX + namespace Platform { namespace FileDialog { + inline bool Supported() { return false; } + inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) { return false; } + +inline QString UrlToLocal(const QUrl &url) { + return url.toLocalFile(); +} + } // namespace FileDialog } // namespace Platform -#endif // else for Q_OS_LINUX + +#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h index 1211acbbe8..195ac8cbcf 100644 --- a/Telegram/SourceFiles/platform/platform_window_title.h +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -26,7 +26,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "platform/mac/window_title_mac.h" #elif defined Q_OS_WIN // Q_OS_MAC #include "platform/win/window_title_win.h" -#elif defined Q_OS_WINRT || defined Q_OS_LINUX +#elif defined Q_OS_WINRT || defined Q_OS_LINUX // Q_OS_MAC || Q_OS_WIN + namespace Platform { inline Window::TitleWidget *CreateTitleWidget(QWidget *parent) { @@ -34,4 +35,5 @@ inline Window::TitleWidget *CreateTitleWidget(QWidget *parent) { } } // namespace Platform + #endif // Q_OS_MAC || Q_OS_WIN || Q_OS_WINRT || Q_OS_LINUX diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index df1b9d1ddc..be99fdbb2c 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "application.h" +#include "platform/platform_file_dialog.h" namespace Profile { namespace { @@ -251,13 +252,12 @@ bool CoverWidget::mimeDataHasImage(const QMimeData *mimeData) const { auto &url = urls.at(0); if (!url.isLocalFile()) return false; - auto file = psConvertFileUrl(url); + auto file = Platform::FileDialog::UrlToLocal(url); QFileInfo info(file); if (info.isDir()) return false; - quint64 s = info.size(); - if (s >= MaxUploadDocumentSize) return false; + if (info.size() > App::kImageSizeLimit) return false; for (auto &ext : cImgExtensions()) { if (file.endsWith(ext, Qt::CaseInsensitive)) { @@ -305,7 +305,7 @@ void CoverWidget::dropEvent(QDropEvent *e) { if (urls.size() == 1) { auto &url = urls.at(0); if (url.isLocalFile()) { - img = App::readImage(psConvertFileUrl(url)); + img = App::readImage(Platform::FileDialog::UrlToLocal(url)); } } } @@ -483,7 +483,7 @@ void CoverWidget::onSetPhoto() { QStringList imgExtensions(cImgExtensions()); QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter()); - _setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter); + _setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_image), filter); } void CoverWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) { diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index b7eea56aa2..82578282ab 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -74,9 +74,6 @@ QAbstractNativeEventFilter *psNativeEventFilter(); void psNewVersion(); void psUpdateOverlayed(QWidget *widget); -inline QString psConvertFileUrl(const QUrl &url) { - return url.toLocalFile(); -} inline QByteArray psDownloadPathBookmark(const QString &path) { return QByteArray(); } diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index e680442bff..062097a5e5 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -443,14 +443,6 @@ void psSendToMenu(bool send, bool silent) { void psUpdateOverlayed(QWidget *widget) { } -QString psConvertFileUrl(const QUrl &url) { - auto urlString = url.toLocalFile(); - if (urlString.startsWith(qsl("/.file/id="))) { - return objc_convertFileUrl(urlString); - } - return urlString; -} - void psDownloadPathEnableAccess() { objc_downloadPathEnableAccess(Global::DownloadPathBookmark()); } diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h index 45d8e69694..de3508e130 100644 --- a/Telegram/SourceFiles/pspecific_mac.h +++ b/Telegram/SourceFiles/pspecific_mac.h @@ -77,7 +77,6 @@ QAbstractNativeEventFilter *psNativeEventFilter(); void psNewVersion(); void psUpdateOverlayed(QWidget *widget); -QString psConvertFileUrl(const QUrl &url); void psDownloadPathEnableAccess(); QByteArray psDownloadPathBookmark(const QString &path); diff --git a/Telegram/SourceFiles/pspecific_mac_p.h b/Telegram/SourceFiles/pspecific_mac_p.h index 5e3219a90c..2bfd8232d6 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.h +++ b/Telegram/SourceFiles/pspecific_mac_p.h @@ -53,7 +53,6 @@ QString objc_appDataPath(); QString objc_downloadPath(); QString objc_currentCountry(); QString objc_currentLang(); -QString objc_convertFileUrl(const QString &url); QByteArray objc_downloadPathBookmark(const QString &path); QByteArray objc_pathBookmark(const QString &path); void objc_downloadPathEnableAccess(const QByteArray &bookmark); diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index b5965b08f5..2f57d50abe 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -203,7 +203,7 @@ void objc_activateWnd(WId winId) { bool objc_handleMediaKeyEvent(void *ev) { auto e = reinterpret_cast(ev); - + int keyCode = (([e data1] & 0xFFFF0000) >> 16); int keyFlags = ([e data1] & 0x0000FFFF); int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA; @@ -999,13 +999,6 @@ QString objc_currentLang() { return currentLang ? NS2QString(currentLang) : QString(); } -QString objc_convertFileUrl(const QString &url) { - NSString *nsurl = [[[NSURL URLWithString: [NSString stringWithUTF8String: (qsl("file://") + url).toUtf8().constData()]] filePathURL] path]; - if (!nsurl) return QString(); - - return NS2QString(nsurl); -} - QByteArray objc_downloadPathBookmark(const QString &path) { #ifndef OS_MAC_STORE return QByteArray(); diff --git a/Telegram/SourceFiles/pspecific_win.h b/Telegram/SourceFiles/pspecific_win.h index 2d9280c21a..127df5e688 100644 --- a/Telegram/SourceFiles/pspecific_win.h +++ b/Telegram/SourceFiles/pspecific_win.h @@ -74,9 +74,6 @@ QAbstractNativeEventFilter *psNativeEventFilter(); void psNewVersion(); void psUpdateOverlayed(TWidget *widget); -inline QString psConvertFileUrl(const QUrl &url) { - return url.toLocalFile(); -} inline QByteArray psDownloadPathBookmark(const QString &path) { return QByteArray(); } diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 69e8380a70..3f9c1e56c3 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -61,7 +61,6 @@ bool gRestartingUpdate = false, gRestarting = false, gRestartingToSettings = fal int32 gLastUpdateCheck = 0; bool gNoStartUpdate = false; bool gStartToSettings = false; -DBIDefaultAttach gDefaultAttach = dbidaDocument; bool gReplaceEmojis = true; bool gCtrlEnter = false; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index d36815245d..82de08c4d7 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -102,7 +102,6 @@ struct TWindowPos { DeclareSetting(TWindowPos, WindowPos); DeclareSetting(bool, SupportTray); DeclareSetting(DBIWorkMode, WorkMode); -DeclareSetting(DBIDefaultAttach, DefaultAttach); DeclareSetting(bool, SeenTrayTooltip); DeclareSetting(bool, RestartingUpdate); DeclareSetting(bool, Restarting); diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index f4a0a18fa6..8fe8e99379 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -151,7 +151,7 @@ void BackgroundRow::updateImage() { p.setRenderHint(QPainter::SmoothPixmapTransform); p.drawPixmap(0, 0, st::settingsBackgroundSize, st::settingsBackgroundSize, pix, sx, sy, s, s); } - imageRound(back, ImageRoundRadius::Small); + Images::prepareRound(back, ImageRoundRadius::Small); _background = App::pixmapFromImageInPlace(std_::move(back)); _background.setDevicePixelRatio(cRetinaFactor()); @@ -215,7 +215,7 @@ void BackgroundWidget::onChooseFromFile() { filters.push_back(qsl("Theme files (*.tdesktop-theme)")); filters.push_back(filedialogAllFilesFilter()); - _chooseFromFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filters.join(qsl(";;"))); + _chooseFromFileQueryId = FileDialog::queryReadFile(lang(lng_choose_image), filters.join(qsl(";;"))); } void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) { diff --git a/Telegram/SourceFiles/settings/settings_cover.cpp b/Telegram/SourceFiles/settings/settings_cover.cpp index e82d5f50e4..368fbe4539 100644 --- a/Telegram/SourceFiles/settings/settings_cover.cpp +++ b/Telegram/SourceFiles/settings/settings_cover.cpp @@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/addcontactbox.h" #include "styles/style_settings.h" #include "styles/style_profile.h" // for divider +#include "platform/platform_file_dialog.h" namespace Settings { @@ -184,13 +185,12 @@ bool CoverWidget::mimeDataHasImage(const QMimeData *mimeData) const { auto &url = urls.at(0); if (!url.isLocalFile()) return false; - auto file = psConvertFileUrl(url); + auto file = Platform::FileDialog::UrlToLocal(url); QFileInfo info(file); if (info.isDir()) return false; - quint64 s = info.size(); - if (s >= MaxUploadDocumentSize) return false; + if (info.size() > App::kImageSizeLimit) return false; for (auto &ext : cImgExtensions()) { if (file.endsWith(ext, Qt::CaseInsensitive)) { @@ -233,7 +233,7 @@ void CoverWidget::dropEvent(QDropEvent *e) { if (urls.size() == 1) { auto &url = urls.at(0); if (url.isLocalFile()) { - img = App::readImage(psConvertFileUrl(url)); + img = App::readImage(Platform::FileDialog::UrlToLocal(url)); } } } @@ -294,10 +294,10 @@ void CoverWidget::refreshStatusText() { } void CoverWidget::onSetPhoto() { - QStringList imgExtensions(cImgExtensions()); - QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter()); + auto imageExtensions = cImgExtensions(); + auto filter = qsl("Image files (*") + imageExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter(); - _setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter); + _setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_image), filter); } void CoverWidget::onEditName() { diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index c44fe6242a..b644cc9be4 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1033,7 +1033,7 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, psOpenFile(filepath); } if (App::main()) App::main()->mediaMarkRead(data); - } else if (data->size < MediaViewImageSizeLimit) { + } else if (data->size < App::kImageSizeLimit) { if (!data->data().isEmpty() && playAnimation) { if (action == ActionOnLoadPlayInline && context && context->getMedia()) { context->getMedia()->playInline(context); @@ -1284,7 +1284,7 @@ void DocumentData::performActionOnLoad() { auto loc = location(true); auto already = loc.name(); auto item = _actionOnLoadMsgId.msg ? App::histItemById(_actionOnLoadMsgId) : nullptr; - bool showImage = !isVideo() && (size < MediaViewImageSizeLimit); + bool showImage = !isVideo() && (size < App::kImageSizeLimit); bool playVoice = voice() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); bool playMusic = song() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); bool playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia(); diff --git a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp index 10b69d6ecf..12caeb0e4b 100644 --- a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp @@ -64,7 +64,7 @@ void NewAvatarButton::paintEvent(QPaintEvent *e) { void NewAvatarButton::setImage(const QImage &image) { auto small = image.scaled(size() * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - imageCircle(small); + Images::prepareCircle(small); _image = App::pixmapFromImageInPlace(std_::move(small)); _image.setDevicePixelRatio(cRetinaFactor()); update(); diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index da9d3a94f4..12870b9667 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -193,7 +193,7 @@ CountrySelectBox::CountrySelectBox() : ItemListBox(st::countriesScroll, st::boxW connect(_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); connect(_inner, SIGNAL(countryChosen(const QString&)), this, SIGNAL(countryChosen(const QString&))); - prepare(); + raiseShadow(); } void CountrySelectBox::onSubmit() { diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp index b9feb2dfd9..3398b996a3 100644 --- a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp @@ -72,7 +72,7 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, } _radiusTo = qRound(sqrt(_radiusTo)); - _show.start(UpdateCallback(_update), 0., 1., _st.showDuration); + _show.start(UpdateCallback(_update), 0., 1., _st.showDuration, anim::easeOutCirc); } RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap &mask, const UpdateCallback &update) diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index 3cf1802bca..0de967d09d 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -26,320 +26,40 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "pspecific.h" +namespace Images { namespace { -using LocalImages = QMap; -LocalImages localImages; - -using WebImages = QMap; -WebImages webImages; - -Image *generateBlankImage() { - auto data = QImage(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - data.fill(Qt::transparent); - data.setDevicePixelRatio(cRetinaFactor()); - return internal::getImage(App::pixmapFromImageInPlace(std_::move(data)), "GIF"); +FORCE_INLINE uint64 blurGetColors(const uchar *p) { + return (uint64)p[0] + ((uint64)p[1] << 16) + ((uint64)p[2] << 32) + ((uint64)p[3] << 48); } -Image *blank() { - static auto blankImage = generateBlankImage(); - return blankImage; -} +const QPixmap &circleMask(int width, int height) { + t_assert(Global::started()); -using StorageImages = QMap; -StorageImages storageImages; + uint64 key = uint64(uint32(width)) << 32 | uint64(uint32(height)); -int64 globalAcquiredSize = 0; - -uint64 PixKey(int width, int height, ImagePixOptions options) { - return static_cast(width) | (static_cast(height) << 24) | (static_cast(options) << 48); -} - -uint64 SinglePixKey(ImagePixOptions options) { - return PixKey(0, 0, options); + Global::CircleMasksMap &masks(Global::RefCircleMasks()); + auto i = masks.constFind(key); + if (i == masks.cend()) { + QImage mask(width, height, QImage::Format_ARGB32_Premultiplied); + { + Painter p(&mask); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(0, 0, width, height, Qt::transparent); + p.setBrush(Qt::white); + p.setPen(Qt::NoPen); + p.drawEllipse(0, 0, width, height); + } + mask.setDevicePixelRatio(cRetinaFactor()); + i = masks.insert(key, App::pixmapFromImageInPlace(std_::move(mask))); + } + return i.value(); } } // namespace -StorageImageLocation StorageImageLocation::Null; - -bool Image::isNull() const { - return (this == blank()); -} - -ImagePtr::ImagePtr() : Parent(blank()) { -} - -ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def) : - Parent((location.type() == mtpc_fileLocation) ? (Image*)(internal::getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) { -} - -Image::Image(const QString &file, QByteArray fmt) : _forgot(false) { - _data = App::pixmapFromImageInPlace(App::readImage(file, &fmt, false, 0, &_saved)); - _format = fmt; - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QByteArray &filecontent, QByteArray fmt) : _forgot(false) { - _data = App::pixmapFromImageInPlace(App::readImage(filecontent, &fmt, false)); - _format = fmt; - _saved = filecontent; - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QPixmap &pixmap, QByteArray format) : _format(format), _forgot(false), _data(pixmap) { - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) : _saved(filecontent), _format(fmt), _forgot(false), _data(pixmap) { - _data = pixmap; - _format = fmt; - _saved = filecontent; - if (!_data.isNull()) { - globalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -const QPixmap &Image::pix(int32 w, int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = ImagePixOption::Smooth | ImagePixOption::None; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(w, h, options); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixRounded(int32 w, int32 h, ImageRoundRadius radius, ImageRoundCorners corners) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = ImagePixOption::Smooth | ImagePixOption::None; - auto cornerOptions = [](ImageRoundCorners corners) { - return (corners & ImageRoundCorner::TopLeft ? ImagePixOption::RoundedTopLeft : ImagePixOption::None) - | (corners & ImageRoundCorner::TopRight ? ImagePixOption::RoundedTopRight : ImagePixOption::None) - | (corners & ImageRoundCorner::BottomLeft ? ImagePixOption::RoundedBottomLeft : ImagePixOption::None) - | (corners & ImageRoundCorner::BottomRight ? ImagePixOption::RoundedBottomRight : ImagePixOption::None); - }; - if (radius == ImageRoundRadius::Large) { - options |= ImagePixOption::RoundedLarge | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Small) { - options |= ImagePixOption::RoundedSmall | cornerOptions(corners); - } - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(w, h, options); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixCircled(int32 w, int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = ImagePixOption::Smooth | ImagePixOption::Circled; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(w, h, options); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurred(int32 w, int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = ImagePixOption::Smooth | ImagePixOption::Blurred; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixNoCache(w, h, options); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = ImagePixOption::Smooth | ImagePixOption::Colored; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixColoredNoCache(add, w, h, true); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 h) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - auto options = ImagePixOption::Blurred | ImagePixOption::Smooth | ImagePixOption::Colored; - auto k = PixKey(w, h, options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend()) { - auto p = pixBlurredColoredNoCache(add, w, h); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - - auto options = ImagePixOption::Smooth | ImagePixOption::None; - auto cornerOptions = [](ImageRoundCorners corners) { - return (corners & ImageRoundCorner::TopLeft ? ImagePixOption::RoundedTopLeft : ImagePixOption::None) - | (corners & ImageRoundCorner::TopRight ? ImagePixOption::RoundedTopRight : ImagePixOption::None) - | (corners & ImageRoundCorner::BottomLeft ? ImagePixOption::RoundedBottomLeft : ImagePixOption::None) - | (corners & ImageRoundCorner::BottomRight ? ImagePixOption::RoundedBottomRight : ImagePixOption::None); - }; - if (radius == ImageRoundRadius::Large) { - options |= ImagePixOption::RoundedLarge | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Small) { - options |= ImagePixOption::RoundedSmall | cornerOptions(corners); - } - - auto k = SinglePixKey(options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { - if (i != _sizesCache.cend()) { - globalAcquiredSize -= int64(i->width()) * i->height() * 4; - } - auto p = pixNoCache(w, h, options, outerw, outerh); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -const QPixmap &Image::pixBlurredSingle(int w, int h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners) const { - checkload(); - - if (w <= 0 || !width() || !height()) { - w = width() * cIntRetinaFactor(); - } else if (cRetina()) { - w *= cIntRetinaFactor(); - h *= cIntRetinaFactor(); - } - - auto options = ImagePixOption::Smooth | ImagePixOption::Blurred; - auto cornerOptions = [](ImageRoundCorners corners) { - return (corners & ImageRoundCorner::TopLeft ? ImagePixOption::RoundedTopLeft : ImagePixOption::None) - | (corners & ImageRoundCorner::TopRight ? ImagePixOption::RoundedTopRight : ImagePixOption::None) - | (corners & ImageRoundCorner::BottomLeft ? ImagePixOption::RoundedBottomLeft : ImagePixOption::None) - | (corners & ImageRoundCorner::BottomRight ? ImagePixOption::RoundedBottomRight : ImagePixOption::None); - }; - if (radius == ImageRoundRadius::Large) { - options |= ImagePixOption::RoundedLarge | cornerOptions(corners); - } else if (radius == ImageRoundRadius::Small) { - options |= ImagePixOption::RoundedSmall | cornerOptions(corners); - } - - auto k = SinglePixKey(options); - auto i = _sizesCache.constFind(k); - if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { - if (i != _sizesCache.cend()) { - globalAcquiredSize -= int64(i->width()) * i->height() * 4; - } - auto p = pixNoCache(w, h, options, outerw, outerh); - if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); - i = _sizesCache.insert(k, p); - if (!p.isNull()) { - globalAcquiredSize += int64(p.width()) * p.height() * 4; - } - } - return i.value(); -} - -namespace { - static inline uint64 _blurGetColors(const uchar *p) { - return (uint64)p[0] + ((uint64)p[1] << 16) + ((uint64)p[2] << 32) + ((uint64)p[3] << 48); - } -} - -QImage imageBlur(QImage img) { +QImage prepareBlur(QImage img) { QImage::Format fmt = img.format(); if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); @@ -379,12 +99,12 @@ QImage imageBlur(QImage img) { int yw = 0; const int we = w - r1; for (y = 0; y < h; y++) { - uint64 cur = _blurGetColors(&pix[yw]); + uint64 cur = blurGetColors(&pix[yw]); uint64 rgballsum = -radius * cur; uint64 rgbsum = cur * ((r1 * (r1 + 1)) >> 1); for (i = 1; i <= radius; i++) { - uint64 cur = _blurGetColors(&pix[yw + i * 4]); + uint64 cur = blurGetColors(&pix[yw + i * 4]); rgbsum += cur * (r1 - i); rgballsum += cur; } @@ -393,7 +113,7 @@ QImage imageBlur(QImage img) { #define update(start, middle, end) \ rgb[y * w + x] = (rgbsum >> 4) & 0x00FF00FF00FF00FFLL; \ -rgballsum += _blurGetColors(&pix[yw + (start) * 4]) - 2 * _blurGetColors(&pix[yw + (middle) * 4]) + _blurGetColors(&pix[yw + (end) * 4]); \ +rgballsum += blurGetColors(&pix[yw + (start) * 4]) - 2 * blurGetColors(&pix[yw + (middle) * 4]) + blurGetColors(&pix[yw + (end) * 4]); \ rgbsum += rgballsum; \ x++; @@ -454,31 +174,7 @@ yi += stride; return img; } -const QPixmap &circleMask(int width, int height) { - t_assert(Global::started()); - - uint64 key = uint64(uint32(width)) << 32 | uint64(uint32(height)); - - Global::CircleMasksMap &masks(Global::RefCircleMasks()); - auto i = masks.constFind(key); - if (i == masks.cend()) { - QImage mask(width, height, QImage::Format_ARGB32_Premultiplied); - { - Painter p(&mask); - p.setRenderHint(QPainter::HighQualityAntialiasing); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, width, height, Qt::transparent); - p.setBrush(Qt::white); - p.setPen(Qt::NoPen); - p.drawEllipse(0, 0, width, height); - } - mask.setDevicePixelRatio(cRetinaFactor()); - i = masks.insert(key, App::pixmapFromImageInPlace(std_::move(mask))); - } - return i.value(); -} - -void imageCircle(QImage &img) { +void prepareCircle(QImage &img) { t_assert(!img.isNull()); img.setDevicePixelRatio(cRetinaFactor()); @@ -491,7 +187,7 @@ void imageCircle(QImage &img) { p.drawPixmap(0, 0, mask); } -void imageRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corners) { +void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corners) { if (!static_cast(corners)) { return; } @@ -508,7 +204,7 @@ void imageRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corner auto imageHeight = image.height(); if (imageWidth < 2 * cornerWidth || imageHeight < 2 * cornerHeight) { if (radius == ImageRoundRadius::Large) { - return imageRound(image, ImageRoundRadius::Small, corners); + return prepareRound(image, ImageRoundRadius::Small, corners); } return; } @@ -550,16 +246,15 @@ void imageRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corner if (corners & ImageRoundCorner::BottomRight) maskCorner(intsBottomRight, masks[3]); } -QImage imageColored(const style::color &add, QImage img) { - QImage::Format fmt = img.format(); - if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { - img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); +QImage prepareColored(const style::color &add, QImage image) { + auto format = image.format(); + if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32_Premultiplied) { + image = std_::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); } - uchar *pix = img.bits(); - if (pix) { + if (auto pix = image.bits()) { int ca = int(add->c.alphaF() * 0xFF), cr = int(add->c.redF() * 0xFF), cg = int(add->c.greenF() * 0xFF), cb = int(add->c.blueF() * 0xFF); - const int w = img.width(), h = img.height(), size = w * h * 4; + const int w = image.width(), h = image.height(), size = w * h * 4; for (int32 i = 0; i < size; i += 4) { int b = pix[i], g = pix[i + 1], r = pix[i + 2], a = pix[i + 3], aca = a * ca; pix[i + 0] = uchar(b + ((aca * (cb - b)) >> 16)); @@ -568,21 +263,39 @@ QImage imageColored(const style::color &add, QImage img) { pix[i + 3] = uchar(a + ((aca * (0xFF - a)) >> 16)); } } - return img; + return std_::move(image); } -QPixmap imagePix(QImage img, int32 w, int32 h, ImagePixOptions options, int32 outerw, int32 outerh) { +QImage prepareOpaque(QImage image) { + if (image.hasAlphaChannel()) { + image = std_::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); + auto ints = reinterpret_cast(image.bits()); + auto bg = anim::shifted(st::imageBgTransparent->c); + auto width = image.width(); + auto height = image.height(); + auto addPerLine = (image.bytesPerLine() / sizeof(uint32)) - width; + for (auto y = 0; y != height; ++y) { + for (auto x = 0; x != width; ++x) { + auto components = anim::shifted(*ints); + *ints = anim::unshifted(components * 256 + bg * (256 - anim::getAlpha(components))); + } + } + } + return std_::move(image); +} + +QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, int outerh) { t_assert(!img.isNull()); - if (options.testFlag(ImagePixOption::Blurred)) { - img = imageBlur(img); + if (options.testFlag(Images::Option::Blurred)) { + img = prepareBlur(img); t_assert(!img.isNull()); } if (w <= 0 || (w == img.width() && (h <= 0 || h == img.height()))) { } else if (h <= 0) { - img = img.scaledToWidth(w, options.testFlag(ImagePixOption::Smooth) ? Qt::SmoothTransformation : Qt::FastTransformation); + img = img.scaledToWidth(w, options.testFlag(Images::Option::Smooth) ? Qt::SmoothTransformation : Qt::FastTransformation); t_assert(!img.isNull()); } else { - img = img.scaled(w, h, Qt::IgnoreAspectRatio, options.testFlag(ImagePixOption::Smooth) ? Qt::SmoothTransformation : Qt::FastTransformation); + img = img.scaled(w, h, Qt::IgnoreAspectRatio, options.testFlag(Images::Option::Smooth) ? Qt::SmoothTransformation : Qt::FastTransformation); t_assert(!img.isNull()); } if (outerw > 0 && outerh > 0) { @@ -603,26 +316,336 @@ QPixmap imagePix(QImage img, int32 w, int32 h, ImagePixOptions options, int32 ou t_assert(!img.isNull()); } } - auto corners = [](ImagePixOptions options) { - return (options.testFlag(ImagePixOption::RoundedTopLeft) ? ImageRoundCorner::TopLeft : ImageRoundCorner::None) - | (options.testFlag(ImagePixOption::RoundedTopRight) ? ImageRoundCorner::TopRight : ImageRoundCorner::None) - | (options.testFlag(ImagePixOption::RoundedBottomLeft) ? ImageRoundCorner::BottomLeft : ImageRoundCorner::None) - | (options.testFlag(ImagePixOption::RoundedBottomRight) ? ImageRoundCorner::BottomRight : ImageRoundCorner::None); + auto corners = [](Images::Options options) { + return (options.testFlag(Images::Option::RoundedTopLeft) ? ImageRoundCorner::TopLeft : ImageRoundCorner::None) + | (options.testFlag(Images::Option::RoundedTopRight) ? ImageRoundCorner::TopRight : ImageRoundCorner::None) + | (options.testFlag(Images::Option::RoundedBottomLeft) ? ImageRoundCorner::BottomLeft : ImageRoundCorner::None) + | (options.testFlag(Images::Option::RoundedBottomRight) ? ImageRoundCorner::BottomRight : ImageRoundCorner::None); }; - if (options.testFlag(ImagePixOption::Circled)) { - imageCircle(img); + if (options.testFlag(Images::Option::Circled)) { + prepareCircle(img); t_assert(!img.isNull()); - } else if (options.testFlag(ImagePixOption::RoundedLarge)) { - imageRound(img, ImageRoundRadius::Large, corners(options)); + } else if (options.testFlag(Images::Option::RoundedLarge)) { + prepareRound(img, ImageRoundRadius::Large, corners(options)); + t_assert(!img.isNull()); + } else if (options.testFlag(Images::Option::RoundedSmall)) { + prepareRound(img, ImageRoundRadius::Small, corners(options)); t_assert(!img.isNull()); - } else if (options.testFlag(ImagePixOption::RoundedSmall)) { - imageRound(img, ImageRoundRadius::Small, corners(options)); } img.setDevicePixelRatio(cRetinaFactor()); - return App::pixmapFromImageInPlace(std_::move(img)); + return std_::move(img); } -QPixmap Image::pixNoCache(int w, int h, ImagePixOptions options, int outerw, int outerh) const { +} // namespace Images + +namespace { + +using LocalImages = QMap; +LocalImages localImages; + +using WebImages = QMap; +WebImages webImages; + +Image *generateBlankImage() { + auto data = QImage(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + data.fill(Qt::transparent); + data.setDevicePixelRatio(cRetinaFactor()); + return internal::getImage(App::pixmapFromImageInPlace(std_::move(data)), "GIF"); +} + +Image *blank() { + static auto blankImage = generateBlankImage(); + return blankImage; +} + +using StorageImages = QMap; +StorageImages storageImages; + +int64 globalAcquiredSize = 0; + +uint64 PixKey(int width, int height, Images::Options options) { + return static_cast(width) | (static_cast(height) << 24) | (static_cast(options) << 48); +} + +uint64 SinglePixKey(Images::Options options) { + return PixKey(0, 0, options); +} + +} // namespace + +StorageImageLocation StorageImageLocation::Null; + +bool Image::isNull() const { + return (this == blank()); +} + +ImagePtr::ImagePtr() : Parent(blank()) { +} + +ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def) : + Parent((location.type() == mtpc_fileLocation) ? (Image*)(internal::getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) { +} + +Image::Image(const QString &file, QByteArray fmt) : _forgot(false) { + _data = App::pixmapFromImageInPlace(App::readImage(file, &fmt, false, 0, &_saved)); + _format = fmt; + if (!_data.isNull()) { + globalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image::Image(const QByteArray &filecontent, QByteArray fmt) : _forgot(false) { + _data = App::pixmapFromImageInPlace(App::readImage(filecontent, &fmt, false)); + _format = fmt; + _saved = filecontent; + if (!_data.isNull()) { + globalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image::Image(const QPixmap &pixmap, QByteArray format) : _format(format), _forgot(false), _data(pixmap) { + if (!_data.isNull()) { + globalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +Image::Image(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) : _saved(filecontent), _format(fmt), _forgot(false), _data(pixmap) { + _data = pixmap; + _format = fmt; + _saved = filecontent; + if (!_data.isNull()) { + globalAcquiredSize += int64(_data.width()) * _data.height() * 4; + } +} + +const QPixmap &Image::pix(int32 w, int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::None; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(w, h, options); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixRounded(int32 w, int32 h, ImageRoundRadius radius, ImageRoundCorners corners) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::None; + auto cornerOptions = [](ImageRoundCorners corners) { + return (corners & ImageRoundCorner::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) + | (corners & ImageRoundCorner::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) + | (corners & ImageRoundCorner::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) + | (corners & ImageRoundCorner::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); + }; + if (radius == ImageRoundRadius::Large) { + options |= Images::Option::RoundedLarge | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Small) { + options |= Images::Option::RoundedSmall | cornerOptions(corners); + } + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(w, h, options); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixCircled(int32 w, int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Circled; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(w, h, options); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurred(int32 w, int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Blurred; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixNoCache(w, h, options); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Smooth | Images::Option::Colored; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixColoredNoCache(add, w, h, true); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 h) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + auto options = Images::Option::Blurred | Images::Option::Smooth | Images::Option::Colored; + auto k = PixKey(w, h, options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + auto p = pixBlurredColoredNoCache(add, w, h); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + + auto options = Images::Option::Smooth | Images::Option::None; + auto cornerOptions = [](ImageRoundCorners corners) { + return (corners & ImageRoundCorner::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) + | (corners & ImageRoundCorner::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) + | (corners & ImageRoundCorner::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) + | (corners & ImageRoundCorner::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); + }; + if (radius == ImageRoundRadius::Large) { + options |= Images::Option::RoundedLarge | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Small) { + options |= Images::Option::RoundedSmall | cornerOptions(corners); + } + + auto k = SinglePixKey(options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { + if (i != _sizesCache.cend()) { + globalAcquiredSize -= int64(i->width()) * i->height() * 4; + } + auto p = pixNoCache(w, h, options, outerw, outerh); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredSingle(int w, int h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners) const { + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + + auto options = Images::Option::Smooth | Images::Option::Blurred; + auto cornerOptions = [](ImageRoundCorners corners) { + return (corners & ImageRoundCorner::TopLeft ? Images::Option::RoundedTopLeft : Images::Option::None) + | (corners & ImageRoundCorner::TopRight ? Images::Option::RoundedTopRight : Images::Option::None) + | (corners & ImageRoundCorner::BottomLeft ? Images::Option::RoundedBottomLeft : Images::Option::None) + | (corners & ImageRoundCorner::BottomRight ? Images::Option::RoundedBottomRight : Images::Option::None); + }; + if (radius == ImageRoundRadius::Large) { + options |= Images::Option::RoundedLarge | cornerOptions(corners); + } else if (radius == ImageRoundRadius::Small) { + options |= Images::Option::RoundedSmall | cornerOptions(corners); + } + + auto k = SinglePixKey(options); + auto i = _sizesCache.constFind(k); + if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) { + if (i != _sizesCache.cend()) { + globalAcquiredSize -= int64(i->width()) * i->height() * 4; + } + auto p = pixNoCache(w, h, options, outerw, outerh); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAcquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +QPixmap Image::pixNoCache(int w, int h, Images::Options options, int outerw, int outerh) const { if (!loading()) const_cast(this)->load(); restore(); @@ -653,23 +676,23 @@ QPixmap Image::pixNoCache(int w, int h, ImagePixOptions options, int outerw, int p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::imageBgTransparent); } - auto corners = [](ImagePixOptions options) { - return (options.testFlag(ImagePixOption::RoundedTopLeft) ? ImageRoundCorner::TopLeft : ImageRoundCorner::None) - | (options.testFlag(ImagePixOption::RoundedTopRight) ? ImageRoundCorner::TopRight : ImageRoundCorner::None) - | (options.testFlag(ImagePixOption::RoundedBottomLeft) ? ImageRoundCorner::BottomLeft : ImageRoundCorner::None) - | (options.testFlag(ImagePixOption::RoundedBottomRight) ? ImageRoundCorner::BottomRight : ImageRoundCorner::None); + auto corners = [](Images::Options options) { + return (options.testFlag(Images::Option::RoundedTopLeft) ? ImageRoundCorner::TopLeft : ImageRoundCorner::None) + | (options.testFlag(Images::Option::RoundedTopRight) ? ImageRoundCorner::TopRight : ImageRoundCorner::None) + | (options.testFlag(Images::Option::RoundedBottomLeft) ? ImageRoundCorner::BottomLeft : ImageRoundCorner::None) + | (options.testFlag(Images::Option::RoundedBottomRight) ? ImageRoundCorner::BottomRight : ImageRoundCorner::None); }; - if (options.testFlag(ImagePixOption::Circled)) { - imageCircle(result); - } else if (options.testFlag(ImagePixOption::RoundedLarge)) { - imageRound(result, ImageRoundRadius::Large, corners(options)); - } else if (options.testFlag(ImagePixOption::RoundedSmall)) { - imageRound(result, ImageRoundRadius::Small, corners(options)); + if (options.testFlag(Images::Option::Circled)) { + Images::prepareCircle(result); + } else if (options.testFlag(Images::Option::RoundedLarge)) { + Images::prepareRound(result, ImageRoundRadius::Large, corners(options)); + } else if (options.testFlag(Images::Option::RoundedSmall)) { + Images::prepareRound(result, ImageRoundRadius::Small, corners(options)); } return App::pixmapFromImageInPlace(std_::move(result)); } - return imagePix(_data.toImage(), w, h, options, outerw, outerh); + return Images::pixmap(_data.toImage(), w, h, options, outerw, outerh); } QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool smooth) const { @@ -678,11 +701,11 @@ QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool if (_data.isNull()) return blank()->pix(); QImage img = _data.toImage(); - if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return App::pixmapFromImageInPlace(imageColored(add, img)); + if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return App::pixmapFromImageInPlace(Images::prepareColored(add, img)); if (h <= 0) { - return App::pixmapFromImageInPlace(imageColored(add, img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); + return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); } - return App::pixmapFromImageInPlace(imageColored(add, img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); + return App::pixmapFromImageInPlace(Images::prepareColored(add, img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation))); } QPixmap Image::pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h) const { @@ -690,14 +713,14 @@ QPixmap Image::pixBlurredColoredNoCache(const style::color &add, int32 w, int32 restore(); if (_data.isNull()) return blank()->pix(); - QImage img = imageBlur(_data.toImage()); + QImage img = Images::prepareBlur(_data.toImage()); if (h <= 0) { img = img.scaledToWidth(w, Qt::SmoothTransformation); } else { img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } - return App::pixmapFromImageInPlace(imageColored(add, img)); + return App::pixmapFromImageInPlace(Images::prepareColored(add, img)); } void Image::forget() const { diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index 460d3c8b39..7049694ff8 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -38,10 +38,6 @@ enum class ImageRoundCorner { Q_DECLARE_FLAGS(ImageRoundCorners, ImageRoundCorner); Q_DECLARE_OPERATORS_FOR_FLAGS(ImageRoundCorners); -QImage imageBlur(QImage image); -void imageRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All); -void imageCircle(QImage &image); - inline uint32 packInt(int32 a) { return (a < 0) ? uint32(int64(a) + 0x100000000LL) : uint32(a); } @@ -124,29 +120,43 @@ inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation return !(a == b); } -enum class ImagePixOption { - None = 0x000, - Smooth = 0x001, - Blurred = 0x002, - Circled = 0x004, - RoundedLarge = 0x008, - RoundedSmall = 0x010, - RoundedTopLeft = 0x020, - RoundedTopRight = 0x040, - RoundedBottomLeft = 0x080, +namespace Images { + +QImage prepareBlur(QImage image); +void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All); +void prepareCircle(QImage &image); +QImage prepareColored(const style::color &add, QImage image); +QImage prepareOpaque(QImage image); + +enum class Option { + None = 0x000, + Smooth = 0x001, + Blurred = 0x002, + Circled = 0x004, + RoundedLarge = 0x008, + RoundedSmall = 0x010, + RoundedTopLeft = 0x020, + RoundedTopRight = 0x040, + RoundedBottomLeft = 0x080, RoundedBottomRight = 0x100, - Colored = 0x200, + Colored = 0x200, }; -Q_DECLARE_FLAGS(ImagePixOptions, ImagePixOption); -Q_DECLARE_OPERATORS_FOR_FLAGS(ImagePixOptions); -QPixmap imagePix(QImage img, int w, int h, ImagePixOptions options, int outerw, int outerh); +Q_DECLARE_FLAGS(Options, Option); +Q_DECLARE_OPERATORS_FOR_FLAGS(Options); + +QImage prepare(QImage img, int w, int h, Options options, int outerw, int outerh); + +inline QPixmap pixmap(QImage img, int w, int h, Options options, int outerw, int outerh) { + return QPixmap::fromImage(prepare(img, w, h, options, outerw, outerh), Qt::ColorOnly); +} + +} // namespace Images class DelayedStorageImage; class HistoryItem; class Image { public: - Image(const QString &file, QByteArray format = QByteArray()); Image(const QByteArray &filecontent, QByteArray format = QByteArray()); Image(const QPixmap &pixmap, QByteArray format = QByteArray()); @@ -183,7 +193,7 @@ public: const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const; const QPixmap &pixSingle(int32 w, int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All) const; const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All) const; - QPixmap pixNoCache(int w = 0, int h = 0, ImagePixOptions options = 0, int outerw = -1, int outerh = -1) const; + QPixmap pixNoCache(int w = 0, int h = 0, Images::Options options = 0, int outerw = -1, int outerh = -1) const; QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const; QPixmap pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h = 0) const; diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index f3b4123cd6..e59e6b6e96 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -160,13 +160,6 @@ public: } } - QPointer weakThis() { - return QPointer(this); - } - QPointer weakThis() const { - return QPointer(this); - } - // Get the size of the widget as it should be. // Negative return value means no default width. virtual int naturalWidth() const { @@ -205,6 +198,18 @@ protected: }; +template +class WeakPointed { +public: + QPointer weak() { + return QPointer(static_cast(this)); + } + QPointer weak() const { + return QPointer(static_cast(this)); + } + +}; + void myEnsureResized(QWidget *target); QPixmap myGrab(TWidget *target, QRect rect = QRect()); QImage myGrabImage(TWidget *target, QRect rect = QRect()); diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 5c75e3a209..748b03d073 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -488,7 +488,7 @@ defaultLinkButton: LinkButton { defaultRippleAnimation: RippleAnimation { color: windowBgRipple; - showDuration: 200; + showDuration: 450; hideDuration: 200; } diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 5fd7192312..3904ac6d34 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -101,30 +101,23 @@ void TopBarWidget::showMenu() { if (auto peer = main->peer()) { if (!_menu) { _menu.create(App::main()); - struct Data { - Ui::DropdownMenu *menu = nullptr; - QPointer that; - }; - auto data = MakeShared(); - data->that = weakThis(); - data->menu = _menu.ptr(); - _menu->setHiddenCallback([this, data] { - data->menu->deleteLater(); - if (data->that && _menu == data->menu) { - _menu = nullptr; - _menuToggle->setForceRippled(false); + _menu->setHiddenCallback([that = weak(), menu = _menu.ptr()] { + menu->deleteLater(); + if (that && that->_menu == menu) { + that->_menu = nullptr; + that->_menuToggle->setForceRippled(false); } }); - _menu->setShowStartCallback([this, data] { - if (data->that && _menu == data->menu) { + _menu->setShowStartCallback(base::lambda_guarded(this, [this, menu = _menu.ptr()] { + if (_menu == menu) { _menuToggle->setForceRippled(true); } - }); - _menu->setHideStartCallback([this, data] { - if (data->that && _menu == data->menu) { + })); + _menu->setHideStartCallback(base::lambda_guarded(this, [this, menu = _menu.ptr()] { + if (_menu == menu) { _menuToggle->setForceRippled(false); } - }); + })); _menuToggle->installEventFilter(_menu); App::main()->fillPeerMenu(peer, [this](const QString &text, base::lambda &&callback) { return _menu->addAction(text, std_::move(callback)); diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index 772608062d..792bd4280d 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -31,7 +31,7 @@ class DropdownMenu; namespace Window { -class TopBarWidget : public TWidget, private base::Subscriber { +class TopBarWidget : public TWidget, private base::Subscriber, public WeakPointed { Q_OBJECT public: diff --git a/Telegram/build/version b/Telegram/build/version index 0b19c091bd..c9c37f6759 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019009 +BetaVersion 10019010 diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index f709915f10..cceb4b8d9e 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -187,10 +187,10 @@ '<(src_loc)/boxes/passcodebox.h', '<(src_loc)/boxes/photocropbox.cpp', '<(src_loc)/boxes/photocropbox.h', - '<(src_loc)/boxes/photosendbox.cpp', - '<(src_loc)/boxes/photosendbox.h', '<(src_loc)/boxes/report_box.cpp', '<(src_loc)/boxes/report_box.h', + '<(src_loc)/boxes/send_files_box.cpp', + '<(src_loc)/boxes/send_files_box.h', '<(src_loc)/boxes/sessionsbox.cpp', '<(src_loc)/boxes/sessionsbox.h', '<(src_loc)/boxes/sharebox.cpp', @@ -364,6 +364,8 @@ '<(src_loc)/platform/linux/main_window_linux.h', '<(src_loc)/platform/linux/notifications_manager_linux.cpp', '<(src_loc)/platform/linux/notifications_manager_linux.h', + '<(src_loc)/platform/mac/file_dialog_mac.mm', + '<(src_loc)/platform/mac/file_dialog_mac.h', '<(src_loc)/platform/mac/mac_utilities.mm', '<(src_loc)/platform/mac/mac_utilities.h', '<(src_loc)/platform/mac/main_window_mac.mm',