Closed beta 10019010: unified attach button, new intro, new stickers.

Now all files sending is confirmed before preparing for sending.
You can paste from clipboard and drag many files at once.
This commit is contained in:
John Preston 2016-11-28 18:45:07 +03:00
parent 26c08236cd
commit 3cff50009c
92 changed files with 1702 additions and 1588 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

View File

@ -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 :(";

View File

@ -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"

View File

@ -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"

View File

@ -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<QImage>(image), Qt::ColorOnly);
return QPixmap::fromImage(std_::move(image), Qt::ColorOnly);
}
void regPhotoItem(PhotoData *data, HistoryItem *item) {

View File

@ -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);

View File

@ -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);

View File

@ -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();
}

View File

@ -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();

View File

@ -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);

View File

@ -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<void()> &&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<TWidget> weakThis;
PeerData *pressed;
};
weakRevokeConfirmBox->setConfirmedCallback([this, data = std_::make_unique<Data>(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);
}
}

View File

@ -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() {

View File

@ -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) {

View File

@ -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;

View File

@ -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() {

View File

@ -124,7 +124,7 @@ void ConfirmPhoneBox::launch() {
connect(&_callTimer, SIGNAL(timeout()), this, SLOT(onCallStatusTimer()));
showChildren();
prepare();
raiseShadow();
Ui::showLayer(this);
}

View File

@ -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) {

View File

@ -172,7 +172,7 @@ void ContactsBox::init() {
_searchTimer.setSingleShot(true);
connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername()));
prepare();
raiseShadow();
}
bool ContactsBox::onSearchByUsername(bool searchCache) {

View File

@ -52,8 +52,6 @@ DownloadPathBox::DownloadPathBox() : AbstractBox()
setPathText(QDir::toNativeSeparators(_path));
}
updateControlsVisibility();
prepare();
}
void DownloadPathBox::updateControlsVisibility() {

View File

@ -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() {

View File

@ -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) {

View File

@ -43,7 +43,6 @@ LocalStorageBox::LocalStorageBox() : AbstractBox()
updateControls();
checkLocalStoredCounts();
prepare();
}
void LocalStorageBox::updateControls() {

View File

@ -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) {

View File

@ -127,8 +127,6 @@ NotificationsBox::NotificationsBox() : AbstractBox()
prepareNotificationSampleSmall();
prepareNotificationSampleLarge();
setMaxHeight(st::notificationsBoxHeight);
prepare();
}
void NotificationsBox::paintEvent(QPaintEvent *e) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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<void(const QStringList &files, bool compressed, const QString &caption, bool ctrlShiftEnter)> &&callback) {
_confirmedCallback = std_::move(callback);
}
void setCancelledCallback(base::lambda<void()> &&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<Ui::InputArea> _caption;
bool _compressedFromSettings;
ChildWidget<Ui::Checkbox> _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<void(const QStringList &files, bool compressed, const QString &caption, bool ctrlShiftEnter)> _confirmedCallback;
base::lambda<void()> _cancelledCallback;
bool _confirmed = false;
ChildWidget<Ui::InputArea> _caption = { nullptr };
ChildWidget<Ui::Checkbox> _compressed = { nullptr };
ChildWidget<Ui::RoundButton> _send;
ChildWidget<Ui::RoundButton> _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 {

View File

@ -52,7 +52,7 @@ SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll)
setLoading(true);
prepare();
raiseShadow();
MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
}

View File

@ -82,7 +82,7 @@ ShareBox::ShareBox(CopyCallback &&copyCallback, SubmitCallback &&submitCallback,
updateButtonsVisibility();
prepare();
raiseShadow();
}
int ShareBox::getTopScrollSkip() const {

View File

@ -265,7 +265,7 @@ void StickersBox::setup() {
rebuildList();
prepare();
raiseShadow();
}
void StickersBox::refreshTabs() {

View File

@ -63,7 +63,7 @@ StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::
onScroll();
prepare();
raiseShadow();
}
void StickerSetBox::onInstalled(uint64 setId) {

View File

@ -57,7 +57,7 @@ _about(st::boxWidth - st::usernamePadding.left()) {
updateLinkText();
prepare();
raiseShadow();
}
void UsernameBox::doSetInnerFocus() {

View File

@ -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

View File

@ -66,9 +66,3 @@ friend Q_DECL_CONSTEXPR QFlags<Flags::enum_type> 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<T*> 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))

View File

@ -25,222 +25,222 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace base {
namespace internal {
template <typename Return, typename ...Args>
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 <typename Return, typename ...Args>
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 <typename Lambda>
using IsLarge = std_::integral_constant<bool, !(sizeof(std_::decay_simple_t<Lambda>) <= kStorageSize)>;
template <typename Lambda>
using IsLarge = std_::integral_constant<bool, !(sizeof(std_::decay_simple_t<Lambda>) <= 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 <typename Return, typename ...Args>
struct lambda_wrap_empty : public lambda_wrap_helper_base<Return, Args...> {
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<Return, Args...>(
&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 <typename Return, typename ...Args>
struct lambda_wrap_empty : public lambda_wrap_helper_base<Return, Args...> {
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<Return, Args...>(
&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<Return, Args...> instance;
static const lambda_wrap_empty<Return, Args...> instance;
};
};
template <typename Return, typename ...Args>
const lambda_wrap_empty<Return, Args...> lambda_wrap_empty<Return, Args...>::instance = {};
template <typename Return, typename ...Args>
const lambda_wrap_empty<Return, Args...> lambda_wrap_empty<Return, Args...>::instance = {};
template <typename Lambda, typename IsLarge, typename Return, typename ...Args> struct lambda_wrap_helper_move_impl;
template <typename Lambda, typename IsLarge, typename Return, typename ...Args> struct lambda_wrap_helper_move_impl;
//
// Disable large lambda support.
// If you really need it, just store data in some std_::unique_ptr<struct>.
//
//template <typename Lambda, typename Return, typename ...Args>
//struct lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
// using JustLambda = std_::decay_simple_t<Lambda>;
// using LambdaPtr = std_::unique_ptr<JustLambda>;
// using Parent = lambda_wrap_helper_base<Return, Args...>;
// static void construct_move_other_method(void *lambda, void *source) {
// auto source_lambda = static_cast<LambdaPtr*>(source);
// new (lambda) LambdaPtr(std_::move(*source_lambda));
// }
// static void construct_move_lambda_method(void *lambda, void *source) {
// auto source_lambda = static_cast<JustLambda*>(source);
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<JustLambda&&>(*source_lambda)));
// }
// static Return call_method(const void *lambda, Args... args) {
// return (**static_cast<const LambdaPtr*>(lambda))(std_::forward<Args>(args)...);
// }
// static void destruct_method(const void *lambda) {
// static_cast<const LambdaPtr*>(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<struct>.
//
//template <typename Lambda, typename Return, typename ...Args>
//struct lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
// using JustLambda = std_::decay_simple_t<Lambda>;
// using LambdaPtr = std_::unique_ptr<JustLambda>;
// using Parent = lambda_wrap_helper_base<Return, Args...>;
// static void construct_move_other_method(void *lambda, void *source) {
// auto source_lambda = static_cast<LambdaPtr*>(source);
// new (lambda) LambdaPtr(std_::move(*source_lambda));
// }
// static void construct_move_lambda_method(void *lambda, void *source) {
// auto source_lambda = static_cast<JustLambda*>(source);
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<JustLambda&&>(*source_lambda)));
// }
// static Return call_method(const void *lambda, Args... args) {
// return (**static_cast<const LambdaPtr*>(lambda))(std_::forward<Args>(args)...);
// }
// static void destruct_method(const void *lambda) {
// static_cast<const LambdaPtr*>(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 <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
using JustLambda = std_::decay_simple_t<Lambda>;
using Parent = lambda_wrap_helper_base<Return, Args...>;
static void construct_move_other_method(void *lambda, void *source) {
auto source_lambda = static_cast<JustLambda*>(source);
new (lambda) JustLambda(static_cast<JustLambda&&>(*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<JustLambda*>(source);
new (lambda) JustLambda(static_cast<JustLambda&&>(*source_lambda));
}
static Return call_method(const void *lambda, Args... args) {
return (*static_cast<const JustLambda*>(lambda))(std_::forward<Args>(args)...);
}
static void destruct_method(const void *lambda) {
static_cast<const JustLambda*>(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 <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> : public lambda_wrap_helper_base<Return, Args...> {
using JustLambda = std_::decay_simple_t<Lambda>;
using Parent = lambda_wrap_helper_base<Return, Args...>;
static void construct_move_other_method(void *lambda, void *source) {
auto source_lambda = static_cast<JustLambda*>(source);
new (lambda) JustLambda(static_cast<JustLambda&&>(*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<JustLambda*>(source);
new (lambda) JustLambda(static_cast<JustLambda&&>(*source_lambda));
}
static Return call_method(const void *lambda, Args... args) {
return (*static_cast<const JustLambda*>(lambda))(std_::forward<Args>(args)...);
}
static void destruct_method(const void *lambda) {
static_cast<const JustLambda*>(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 <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_move : public lambda_wrap_helper_move_impl<Lambda
, typename lambda_wrap_helper_base<Return, Args...>::template IsLarge<Lambda>
, Return, Args...> {
static const lambda_wrap_helper_move instance;
};
template <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_move : public lambda_wrap_helper_move_impl<Lambda
, typename lambda_wrap_helper_base<Return, Args...>::template IsLarge<Lambda>
, Return, Args...> {
static const lambda_wrap_helper_move instance;
};
template <typename Lambda, typename Return, typename ...Args>
const lambda_wrap_helper_move<Lambda, Return, Args...> lambda_wrap_helper_move<Lambda, Return, Args...>::instance = {};
template <typename Lambda, typename Return, typename ...Args>
const lambda_wrap_helper_move<Lambda, Return, Args...> lambda_wrap_helper_move<Lambda, Return, Args...>::instance = {};
template <typename Lambda, typename IsLarge, typename Return, typename ...Args> struct lambda_wrap_helper_copy_impl;
template <typename Lambda, typename IsLarge, typename Return, typename ...Args> struct lambda_wrap_helper_copy_impl;
//
// Disable large lambda support.
// If you really need it, just store data in some QSharedPointer<struct>.
//
//template <typename Lambda, typename Return, typename ...Args>
//struct lambda_wrap_helper_copy_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> {
// using JustLambda = std_::decay_simple_t<Lambda>;
// using LambdaPtr = std_::unique_ptr<JustLambda>;
// using Parent = lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...>;
// static void construct_copy_other_method(void *lambda, const void *source) {
// auto source_lambda = static_cast<const LambdaPtr*>(source);
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(*source_lambda->get()));
// }
// static void construct_copy_lambda_method(void *lambda, const void *source) {
// auto source_lambda = static_cast<const JustLambda*>(source);
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<const JustLambda &>(*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<struct>.
//
//template <typename Lambda, typename Return, typename ...Args>
//struct lambda_wrap_helper_copy_impl<Lambda, std_::true_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...> {
// using JustLambda = std_::decay_simple_t<Lambda>;
// using LambdaPtr = std_::unique_ptr<JustLambda>;
// using Parent = lambda_wrap_helper_move_impl<Lambda, std_::true_type, Return, Args...>;
// static void construct_copy_other_method(void *lambda, const void *source) {
// auto source_lambda = static_cast<const LambdaPtr*>(source);
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(*source_lambda->get()));
// }
// static void construct_copy_lambda_method(void *lambda, const void *source) {
// auto source_lambda = static_cast<const JustLambda*>(source);
// new (lambda) LambdaPtr(std_::make_unique<JustLambda>(static_cast<const JustLambda &>(*source_lambda)));
// }
// lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) {
// }
//
//};
template <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_copy_impl<Lambda, std_::false_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> {
using JustLambda = std_::decay_simple_t<Lambda>;
using Parent = lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...>;
static void construct_copy_other_method(void *lambda, const void *source) {
auto source_lambda = static_cast<const JustLambda*>(source);
new (lambda) JustLambda(static_cast<const JustLambda &>(*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<const JustLambda*>(source);
new (lambda) JustLambda(static_cast<const JustLambda &>(*source_lambda));
}
lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) {
}
template <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_copy_impl<Lambda, std_::false_type, Return, Args...> : public lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...> {
using JustLambda = std_::decay_simple_t<Lambda>;
using Parent = lambda_wrap_helper_move_impl<Lambda, std_::false_type, Return, Args...>;
static void construct_copy_other_method(void *lambda, const void *source) {
auto source_lambda = static_cast<const JustLambda*>(source);
new (lambda) JustLambda(static_cast<const JustLambda &>(*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<const JustLambda*>(source);
new (lambda) JustLambda(static_cast<const JustLambda &>(*source_lambda));
}
lambda_wrap_helper_copy_impl() : Parent(&lambda_wrap_helper_copy_impl::construct_copy_other_method) {
}
};
};
template <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_copy : public lambda_wrap_helper_copy_impl<Lambda
, typename lambda_wrap_helper_base<Return, Args...>::template IsLarge<Lambda>
, Return, Args...> {
static const lambda_wrap_helper_copy instance;
};
template <typename Lambda, typename Return, typename ...Args>
struct lambda_wrap_helper_copy : public lambda_wrap_helper_copy_impl<Lambda
, typename lambda_wrap_helper_base<Return, Args...>::template IsLarge<Lambda>
, Return, Args...> {
static const lambda_wrap_helper_copy instance;
};
template <typename Lambda, typename Return, typename ...Args>
const lambda_wrap_helper_copy<Lambda, Return, Args...> lambda_wrap_helper_copy<Lambda, Return, Args...>::instance = {};
template <typename Lambda, typename Return, typename ...Args>
const lambda_wrap_helper_copy<Lambda, Return, Args...> lambda_wrap_helper_copy<Lambda, Return, Args...>::instance = {};
} // namespace internal
@ -262,6 +262,8 @@ class lambda<Return(Args...)> {
using IsRvalue = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::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 <typename FunctionType>
struct lambda_type_helper;
template <typename Lambda, typename R, typename ...Args>
struct lambda_type_helper<R(Lambda::*)(Args...) const> {
using type = lambda<R(Args...)>;
};
} // namespace internal
template <typename FunctionType>
using lambda_type = typename internal::lambda_type_helper<decltype(&FunctionType::operator())>::type;
// Guard lambda call by one or many QObject* weak pointers.
namespace internal {
template <int N>
class lambda_guard_creator;
template <int N, typename Lambda>
class lambda_guard_data {
public:
using return_type = typename lambda_type<Lambda>::return_type;
template <typename ...PointersAndLambda>
lambda_guard_data(PointersAndLambda&&... qobjectsAndLambda) : _lambda(init(_pointers, std_::forward<PointersAndLambda>(qobjectsAndLambda)...)) {
}
template <typename ...Args>
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>(args)...);
}
private:
template <typename ...PointersAndLambda>
Lambda init(QPointer<QObject> *pointers, QObject *qobject, PointersAndLambda&&... qobjectsAndLambda) {
*pointers = qobject;
return init(++pointers, std_::forward<PointersAndLambda>(qobjectsAndLambda)...);
}
Lambda init(QPointer<QObject> *pointers, Lambda &&lambda) {
return std_::move(lambda);
}
QPointer<QObject> _pointers[N];
Lambda _lambda;
};
template <int N, typename Lambda>
class lambda_guard {
public:
using return_type = typename lambda_type<Lambda>::return_type;
template <typename ...PointersAndLambda>
lambda_guard(PointersAndLambda&&... qobjectsAndLambda) : _data(std_::make_unique<lambda_guard_data<N, Lambda>>(std_::forward<PointersAndLambda>(qobjectsAndLambda)...)) {
static_assert(sizeof...(PointersAndLambda) == N + 1, "Wrong argument count!");
}
template <typename ...Args>
inline return_type operator()(Args... args) const {
return (*_data)(std_::forward<Args>(args)...);
}
private:
std_::unique_ptr<lambda_guard_data<N, Lambda>> _data;
};
template <int N, int K, typename ...PointersAndLambda>
struct lambda_guard_type;
template <int N, int K, typename Pointer, typename ...PointersAndLambda>
struct lambda_guard_type<N, K, Pointer, PointersAndLambda...> {
using type = typename lambda_guard_type<N, K - 1, PointersAndLambda...>::type;
};
template <int N, typename Lambda>
struct lambda_guard_type<N, 0, Lambda> {
using type = lambda_guard<N, Lambda>;
};
template <typename ...PointersAndLambda>
struct lambda_guard_type_helper {
static constexpr int N = sizeof...(PointersAndLambda);
using type = typename lambda_guard_type<N - 1, N - 1, PointersAndLambda...>::type;
};
template <typename ...PointersAndLambda>
using lambda_guard_t = typename lambda_guard_type_helper<PointersAndLambda...>::type;
} // namespace internal
template <typename ...PointersAndLambda>
inline internal::lambda_guard_t<PointersAndLambda...> lambda_guarded(PointersAndLambda&&... qobjectsAndLambda) {
static_assert(sizeof...(PointersAndLambda) > 0, "Lambda should be passed here.");
return internal::lambda_guard_t<PointersAndLambda...>(std_::forward<PointersAndLambda>(qobjectsAndLambda)...);
}
// Pass lambda instead of a Qt void() slot.
class lambda_slot_wrap : public QObject {
Q_OBJECT

View File

@ -120,7 +120,7 @@ public:
if (!_data) {
_data = MakeShared<ObservableData<EventType, Handler>>(this);
}
return _data->append(std_::forward<Handler>(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>(handler));
auto node = new Node(_observable->_data, std_::move(handler));
if (_begin) {
_end->next = node;
node->prev = _end;

View File

@ -158,14 +158,9 @@ template <typename T>
struct add_const {
using type = const T;
};
template <typename T>
using add_const_t = typename add_const<T>::type;
template <typename T>
constexpr add_const_t<T> &as_const(T& t) noexcept {
return t;
}
template <typename T>
void as_const(const T&&) = delete;
// This is not full unique_ptr, but at least with std interface.
template <typename T>

View File

@ -47,6 +47,11 @@ inline constexpr D up_cast_helper(std_::false_type, T object) {
return nullptr;
}
template <typename T>
constexpr std_::add_const_t<T> &any_as_const(T &&value) noexcept {
return value;
}
} // namespace internal
template <typename D, typename T>
@ -83,6 +88,12 @@ scope_guard_helper<Lambda> 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<T*> 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 <typename Enum>
inline QFlags<Enum> qFlags(Enum v) {
return QFlags<Enum>(v);
@ -352,11 +363,6 @@ enum DBIConnectionType {
dbictTcpProxy = 3,
};
enum DBIDefaultAttach {
dbidaDocument = 0,
dbidaPhoto = 1,
};
struct ProxyData {
QString host;
uint32 port = 0;

View File

@ -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";

View File

@ -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;

View File

@ -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 {

View File

@ -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 }};

View File

@ -116,8 +116,8 @@ void DragArea::dragLeaveEvent(QDragLeaveEvent *e) {
void DragArea::dropEvent(QDropEvent *e) {
static_cast<HistoryWidget*>(parentWidget())->dropEvent(e);
if (e->isAccepted()) {
emit dropped(e->mimeData());
if (e->isAccepted() && _droppedCallback) {
_droppedCallback(e->mimeData());
}
}

View File

@ -48,6 +48,10 @@ public:
void hideFast();
void setDroppedCallback(base::lambda<void(const QMimeData *data)> &&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<void(const QMimeData *data)> _droppedCallback;
anim::fvalue a_opacity;
anim::fvalue a_colorDrop;

View File

@ -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<StylePtr>(s)) {
, _st(std_::move(s)) {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
_rows.reserve(markup->rows.size());
for (int i = 0, l = markup->rows.size(); i != l; ++i) {

View File

@ -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<QImage>(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<FileLoadTask>(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<QUrl>::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<QImage>(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<QImage>(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 <typename SendCallback>
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 <typename Callback>
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<QUrl> &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<QImage>(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<QUrl> &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<FileLoadTask>(content, image, type, to, caption));
} else {
tasks.push_back(MakeShared<FileLoadTask>(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<FileLoadTask>(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<QUrl> &urls(d->urls());
if (urls.isEmpty()) return QStringList();
files.reserve(urls.size());
for (QList<QUrl>::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);

View File

@ -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<QUrl> &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<QUrl> nonLocalUrls;
QStringList directories;
QStringList emptyFiles;
QStringList tooLargeFiles;
QStringList filesToSend;
bool allFilesArePhotos = true;
};
SendingFilesLists getSendingFilesLists(const QList<QUrl> &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 <typename Callback>
bool validateSendingFiles(const SendingFilesLists &lists, Callback callback);
template <typename SendCallback>
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<Ui::InnerDropdown> _membersDropdown = { nullptr };
QTimer _membersDropdownShowTimer;
ChildWidget<Ui::DropdownMenu> _attachType;
ChildWidget<EmojiPan> _emojiPan;
DragState _attachDrag = DragStateNone;
ChildWidget<DragArea> _attachDragDocument, _attachDragPhoto;
@ -1142,8 +1159,6 @@ private:
int64 _serviceImageCacheSize = 0;
QString _confirmSource;
uint64 _confirmWithTextId = 0;
QString _titlePeerText;
bool _titlePeerTextOnline = false;
int _titlePeerTextWidth = 0;

View File

@ -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();

View File

@ -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);

View File

@ -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<uint64>())
FileLoadTask::FileLoadTask(const QString &filepath, SendMediaType type, const FileLoadTo &to, const QString &caption) : _id(rand_value<uint64>())
, _to(to)
, _filepath(filepath)
, _type(type)
, _confirm(confirm) {
, _caption(caption) {
}
FileLoadTask::FileLoadTask(const QByteArray &content, PrepareMediaType type, const FileLoadTo &to) : _id(rand_value<uint64>())
FileLoadTask::FileLoadTask(const QByteArray &content, const QImage &image, SendMediaType type, const FileLoadTo &to, const QString &caption) : _id(rand_value<uint64>())
, _to(to)
, _content(content)
, _type(type) {
}
FileLoadTask::FileLoadTask(const QImage &image, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm, const QString &originalText) : _id(rand_value<uint64>())
, _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<uint64>())
FileLoadTask::FileLoadTask(const QByteArray &voice, int32 duration, const VoiceWaveform &waveform, const FileLoadTo &to, const QString &caption) : _id(rand_value<uint64>())
, _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<FileLoadResult>(_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<MTPDocumentAttribute>(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);
}
}

View File

@ -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<PhotoId>()), 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<PhotoId>()), file(file), peer(peer), type(type), replyTo(replyTo) {
}
ToPrepareMedia(const QImage &img, const PeerId &peer, PrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(rand_value<PhotoId>()), 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<PhotoId>()), img(img), peer(peer), type(type), replyTo(replyTo) {
}
ToPrepareMedia(const QByteArray &data, const PeerId &peer, PrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(rand_value<PhotoId>()), 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<PhotoId>()), 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<PhotoId>()), 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<PhotoId>()), 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<ToPrepareMedia> ToPrepareMedias;
typedef QMap<int32, QByteArray> 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<SendMediaPrepare>;
using UploadFileParts = QMap<int, QByteArray>;
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<TaskId>(const_cast<Task*>(this));
}
};
typedef QSharedPointer<Task> TaskPtr;
typedef QList<TaskPtr> TasksList;
using TaskPtr = QSharedPointer<Task>;
using TasksList = QList<TaskPtr>;
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<FileLoadResult> 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;

View File

@ -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<ImageLoadTask>(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<StickerImageLoadTask>(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<AudioLoadTask>(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<WebFileLoadTask>(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<CountWaveformTask>(document));
memcpy(voice->waveform.data() + 1, &taskId, sizeof(taskId));
}
}

View File

@ -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) {

View File

@ -212,10 +212,7 @@ public:
QRect historyRect() const;
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params);
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();

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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<Media::Clip::Reader>(_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();

View File

@ -94,7 +94,7 @@ enum LocalLoadStatus {
LocalFailed,
};
typedef void *TaskId; // no interface, just id
using TaskId = void*; // no interface, just id
enum LoadFromCloudSetting {
LoadFromCloudOrLocal,

View File

@ -827,17 +827,6 @@ protected:
typedef void (*MTPStateChangedHandler)(int32 dcId, int32 state);
typedef void(*MTPSessionResetHandler)(int32 dcId);
template <typename FunctionType>
struct LambdaUniqueHelper;
template <typename Lambda, typename R, typename ...Args>
struct LambdaUniqueHelper<R(Lambda::*)(Args...) const> {
using UniqueType = base::lambda<R(Args...)>;
};
template <typename FunctionType>
using LambdaGetUnique = typename LambdaUniqueHelper<FunctionType>::UniqueType;
template <typename Base, typename FunctionType>
class RPCHandlerImplementation : public Base {
protected:
@ -948,7 +937,7 @@ inline RPCDoneHandlerPtr rpcDone_lambda_wrap_helper(base::lambda<R(mtpRequestId)
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
RPCDoneHandlerPtr rpcDone(Lambda &&lambda) {
return rpcDone_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
return rpcDone_lambda_wrap_helper(base::lambda_type<Lambda>(std_::move(lambda)));
}
template <typename FunctionType>
@ -1008,5 +997,5 @@ inline RPCFailHandlerPtr rpcFail_lambda_wrap_helper(base::lambda<bool(mtpRequest
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
RPCFailHandlerPtr rpcFail(Lambda &&lambda) {
return rpcFail_lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
return rpcFail_lambda_wrap_helper(base::lambda_type<Lambda>(std_::move(lambda)));
}

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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 <Cocoa/Cocoa.h>
#include <CoreFoundation/CFURL.h>
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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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();
}

View File

@ -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());
}

View File

@ -77,7 +77,6 @@ QAbstractNativeEventFilter *psNativeEventFilter();
void psNewVersion();
void psUpdateOverlayed(QWidget *widget);
QString psConvertFileUrl(const QUrl &url);
void psDownloadPathEnableAccess();
QByteArray psDownloadPathBookmark(const QString &path);

View File

@ -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);

View File

@ -203,7 +203,7 @@ void objc_activateWnd(WId winId) {
bool objc_handleMediaKeyEvent(void *ev) {
auto e = reinterpret_cast<NSEvent*>(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();

View File

@ -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();
}

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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() {

View File

@ -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();

View File

@ -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();

View File

@ -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() {

View File

@ -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)

View File

@ -26,320 +26,40 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "pspecific.h"
namespace Images {
namespace {
using LocalImages = QMap<QString, Image*>;
LocalImages localImages;
using WebImages = QMap<QString, WebImage*>;
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<StorageKey, StorageImage*>;
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<uint64>(width) | (static_cast<uint64>(height) << 24) | (static_cast<uint64>(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<int>(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<uint32*>(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<QString, Image*>;
LocalImages localImages;
using WebImages = QMap<QString, WebImage*>;
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<StorageKey, StorageImage*>;
StorageImages storageImages;
int64 globalAcquiredSize = 0;
uint64 PixKey(int width, int height, Images::Options options) {
return static_cast<uint64>(width) | (static_cast<uint64>(height) << 24) | (static_cast<uint64>(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<Image*>(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 {

View File

@ -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;

View File

@ -160,13 +160,6 @@ public:
}
}
QPointer<TWidget> weakThis() {
return QPointer<TWidget>(this);
}
QPointer<const TWidget> weakThis() const {
return QPointer<const TWidget>(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 <typename Widget>
class WeakPointed {
public:
QPointer<Widget> weak() {
return QPointer<Widget>(static_cast<Widget*>(this));
}
QPointer<const Widget> weak() const {
return QPointer<const Widget>(static_cast<const Widget*>(this));
}
};
void myEnsureResized(QWidget *target);
QPixmap myGrab(TWidget *target, QRect rect = QRect());
QImage myGrabImage(TWidget *target, QRect rect = QRect());

View File

@ -488,7 +488,7 @@ defaultLinkButton: LinkButton {
defaultRippleAnimation: RippleAnimation {
color: windowBgRipple;
showDuration: 200;
showDuration: 450;
hideDuration: 200;
}

View File

@ -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<TWidget> that;
};
auto data = MakeShared<Data>();
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<void()> &&callback) {
return _menu->addAction(text, std_::move(callback));

View File

@ -31,7 +31,7 @@ class DropdownMenu;
namespace Window {
class TopBarWidget : public TWidget, private base::Subscriber {
class TopBarWidget : public TWidget, private base::Subscriber, public WeakPointed<TopBarWidget> {
Q_OBJECT
public:

View File

@ -3,4 +3,4 @@ AppVersionStrMajor 0.10
AppVersionStrSmall 0.10.20
AppVersionStr 0.10.20
AlphaChannel 0
BetaVersion 10019009
BetaVersion 10019010

View File

@ -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',