diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 333c36d04..34a08a0c6 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -121,6 +121,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_bad_name" = "Please enter your first and last name."; "lng_bad_photo" = "Bad image selected."; +"lng_bad_image_for_photo" = "This image can't be sent that way.\nWould you like to send it as a document?"; + "lng_signup_title" = "Information and photo"; "lng_signup_desc" = "Please enter your name and\nupload a photo."; @@ -326,6 +328,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_in_dlg_audio" = "Audio"; "lng_in_dlg_document" = "Document"; "lng_in_dlg_sticker" = "Sticker"; +"lng_in_dlg_sticker_emoji" = "{emoji} (sticker)"; "lng_send_button" = "Send"; "lng_message_ph" = "Write a message.."; @@ -450,6 +453,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Terminate other sessions.\n\nThanks,\nThe Telegram Team"; "lng_new_version7005" = "Telegram Desktop was updated to version {version}\n\n — Stickers support\n — Local caching for voice messages\n — Added Portuguese language\n\nFull version history is available here:\n{link}"; +"lng_new_version7006_appstore" = "Telegram Desktop was updated to version {version}\n\n — Stickers support\n — Local caching for voice messages\n — Added new languages\n\nFull version history is available here:\n{link}"; // Mac specific diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 4fa343db8..4f005113a 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1627,7 +1627,7 @@ btnContext: iconedButton(btnDefIconed) { opacity: 1; overOpacity: 1; - width: 172px; + width: -32px; height: 36px; color: black; @@ -1655,9 +1655,9 @@ mediaviewLoader: size(78px, 33px); mediaviewLoaderPoint: size(9px, 9px); mediaviewLoaderSkip: 9px; -minPhotoWidth: 90px; -minPhotoHeight: 90px; +minPhotoSize: 90px; maxMediaSize: 420px; +maxStickerSize: 256px; usernameFont: font(14px); usernameColor: #777; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 8a408ff38..bab7871c8 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1292,6 +1292,8 @@ namespace App { documentsData.clear(); cSetRecentStickers(RecentStickerPack()); cSetStickersHash(QByteArray()); + cSetStickers(AllStickers()); + cSetEmojiStickers(EmojiStickersMap()); ::videoItems.clear(); ::audioItems.clear(); ::documentItems.clear(); @@ -2013,7 +2015,7 @@ namespace App { } exif_data_free(exifData); } - } else if (opaque) { + } else if (opaque && result.hasAlphaChannel()) { QImage solid(result.width(), result.height(), QImage::Format_ARGB32_Premultiplied); solid.fill(st::white->c); { diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 3a28d2489..868571f8c 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -479,7 +479,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId) int32 filesize = 0; QByteArray data; - ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false); + ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false); connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection); diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 07fa7ba0b..030f34e36 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -89,6 +89,7 @@ void ConfirmBox::mousePressEvent(QMouseEvent *e) { textlnkDown(textlnkOver()); update(); } + return LayeredWidget::mousePressEvent(e); } void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp index 15f49a341..fd975ccba 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.cpp +++ b/Telegram/SourceFiles/boxes/photocropbox.cpp @@ -57,7 +57,7 @@ PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : _downState(0 } void PhotoCropBox::mousePressEvent(QMouseEvent *e) { - if (e->button() != Qt::LeftButton) return; + if (e->button() != Qt::LeftButton) return LayeredWidget::mousePressEvent(e); _downState = mouseState(e->pos()); _fromposx = e->pos().x(); @@ -65,6 +65,8 @@ void PhotoCropBox::mousePressEvent(QMouseEvent *e) { _fromcropx = _cropx; _fromcropy = _cropy; _fromcropw = _cropw; + + return LayeredWidget::mousePressEvent(e); } int32 PhotoCropBox::mouseState(QPoint p) { diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 2a87f5e17..cd8bdfdec 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -86,7 +86,9 @@ enum { AudioVoiceMsgChannels = 2, // stereo AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded - StickerInMemory = 128 * 1024, // 128 Kb stickers hold in memory, auto loaded and displayed inline + + StickerInMemory = 256 * 1024, // 128 Kb stickers hold in memory, auto loaded and displayed inline + StickerMaxSize = 1280, // 1024x1024 is a max image size for sticker MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app MaxZoomLevel = 7, // x8 diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index a1697b610..c69803f2d 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -365,13 +365,15 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { } float64 coef = qMin((stickerWidth - st::stickerPanPadding * 2) / float64(sticker->dimensions.width()), (stickerSize - st::stickerPanPadding * 2) / float64(sticker->dimensions.height())); + if (coef > 1) coef = 1; int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); + if (w < 1) w = 1; + if (h < 1) h = 1; QPoint ppos = pos + QPoint((stickerSize - w) / 2, (stickerSize - h) / 2); - if (sticker->sticker->isNull()) { - p.drawPixmap(ppos, sticker->thumb->pix(w)); + p.drawPixmap(ppos, sticker->thumb->pix(w, h)); } else { - p.drawPixmap(ppos, sticker->sticker->pix(w)); + p.drawPixmap(ppos, sticker->sticker->pix(w, h)); } if (hover > 0 && _isUserGen[index]) { diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index 02cab32a0..763ab4a22 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -116,7 +116,7 @@ void FileUploader::sendNext() { MTPInputFile doc = (i->docSize > UseBigFilesFrom) ? MTP_inputFileBig(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename)) : MTP_inputFile(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename), MTP_string(docMd5)); if (i->partsCount) { - emit thumbDocumentReady(uploading, doc, MTP_inputFile(MTP_long(i->media.jpeg_id), MTP_int(i->partsCount), MTP_string(i->media.filename), MTP_string(i->media.jpeg_md5))); + emit thumbDocumentReady(uploading, doc, MTP_inputFile(MTP_long(i->media.thumbId), MTP_int(i->partsCount), MTP_string(qsl("thumb.") + i->media.thumbExt), MTP_string(i->media.jpeg_md5))); } else { emit documentReady(uploading, doc); } @@ -166,7 +166,7 @@ void FileUploader::sendNext() { } else { LocalFileParts::iterator part = i->media.parts.begin(); - mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.jpeg_id), MTP_int(part.key()), MTP_string(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]); + mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.thumbId), MTP_int(part.key()), MTP_string(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]); requestsSent.insert(requestId, part.value()); dcMap.insert(requestId, todc); sentSize += part.value().size(); diff --git a/Telegram/SourceFiles/gui/contextmenu.cpp b/Telegram/SourceFiles/gui/contextmenu.cpp index 9bc88d973..4e370a52d 100644 --- a/Telegram/SourceFiles/gui/contextmenu.cpp +++ b/Telegram/SourceFiles/gui/contextmenu.cpp @@ -47,6 +47,7 @@ QAction *ContextMenu::addAction(const QString &text, const QObject *receiver, co connect(b, SIGNAL(stateChanged(int,ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int,ButtonStateChangeSource))); _width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + b->width())); + for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - int(st::dropdownPadding.left() + st::dropdownPadding.right()), _buttons[i]->height()); _height += b->height(); resize(_width, _height); @@ -61,6 +62,8 @@ ContextMenu::Actions &ContextMenu::actions() { void ContextMenu::actionChanged() { for (int32 i = 0, l = _actions.size(); i < l; ++i) { _buttons[i]->setText(_actions[i]->text()); + _width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + _buttons[i]->width())); + _buttons[i]->resize(_width - int(st::dropdownPadding.left() + st::dropdownPadding.right()), _buttons[i]->height()); } } diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp index ba97c1233..32c2607ff 100644 --- a/Telegram/SourceFiles/gui/flattextarea.cpp +++ b/Telegram/SourceFiles/gui/flattextarea.cpp @@ -297,21 +297,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const { uint32 index = imageName.mid(8).toUInt(0, 16); const EmojiData *emoji = getEmoji(index); if (emoji) { - emojiText.reserve(emoji->len + (emoji->postfix ? 1 : 0)); - switch (emoji->len) { - case 1: emojiText.append(QChar(emoji->code & 0xFFFF)); break; - case 2: - emojiText.append(QChar((emoji->code >> 16) & 0xFFFF)); - emojiText.append(QChar(emoji->code & 0xFFFF)); - break; - case 4: - emojiText.append(QChar((emoji->code >> 16) & 0xFFFF)); - emojiText.append(QChar(emoji->code & 0xFFFF)); - emojiText.append(QChar((emoji->code2 >> 16) & 0xFFFF)); - emojiText.append(QChar(emoji->code2 & 0xFFFF)); - break; - } - if (emoji->postfix) emojiText.append(QChar(emoji->postfix)); + emojiText = textEmojiString(emoji); } } } diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 0268c9d74..3919ce516 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -79,7 +79,7 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const { w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x8000000000000000LL | (uint64(w) << 32) | uint64(h); + uint64 k = 0x1000000000000000LL | (uint64(w) << 32) | uint64(h); Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend()) { QPixmap p(pixBlurredNoCache(w, h)); @@ -92,6 +92,52 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const { return i.value(); } +const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) const { + restore(); + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + uint64 k = 0x2000000000000000LL | (uint64(w) << 32) | uint64(h); + Sizes::const_iterator i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + QPixmap p(pixColoredNoCache(add, w, h, true)); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 h) const { + restore(); + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + uint64 k = 0x3000000000000000LL | (uint64(w) << 32) | uint64(h); + Sizes::const_iterator i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + QPixmap p(pixBlurredColoredNoCache(add, w, h)); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + const QPixmap &Image::pixSingle(int32 w, int32 h) const { restore(); checkload(); @@ -128,7 +174,7 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const { w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x8000000000000000LL | 0LL; + uint64 k = 0x1000000000000000LL | 0LL; Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) { if (i != _sizesCache.cend()) { @@ -146,29 +192,40 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const { namespace { static inline uint64 _blurGetColors(const uchar *p) { - return p[0] + (p[1] << 16) + ((uint64)p[2] << 32); + return (uint64)p[0] + ((uint64)p[1] << 16) + ((uint64)p[2] << 32) + ((uint64)p[3] << 48); } } QImage imageBlur(QImage img) { QImage::Format fmt = img.format(); - if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { - QImage tmp(img.width(), img.height(), QImage::Format_ARGB32); - { - QPainter p(&tmp); - p.drawImage(0, 0, img); - } - img = tmp; + if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { + img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); } uchar *pix = img.bits(); if (pix) { - const int w = img.width(), h = img.height(), stride = w * 4; - const int radius = 7; + int w = img.width(), h = img.height(), wold = w, hold = h; + const int radius = 3; const int r1 = radius + 1; const int div = radius * 2 + 1; - + const int stride = w * 4; if (radius < 16 && div < w && div < h && stride <= w * 4) { + bool withalpha = img.hasAlphaChannel(); + if (withalpha) { + QImage imgsmall(w, h, img.format()); + { + QPainter p(&imgsmall); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.setRenderHint(QPainter::SmoothPixmapTransform); + p.fillRect(0, 0, w, h, st::transparent->b); + p.drawImage(QRect(radius, radius, w - 2 * radius, h - 2 * radius), img, QRect(0, 0, w, h)); + } + QImage was = img; + img = imgsmall; + imgsmall = QImage(); + pix = img.bits(); + if (!pix) return was; + } uint64 *rgb = new uint64[w * h]; int x, y, i; @@ -189,7 +246,7 @@ QImage imageBlur(QImage img) { x = 0; #define update(start, middle, end) \ -rgb[y * w + x] = (rgbsum >> 6) & 0x00FF00FF00FF00FFLL; \ +rgb[y * w + x] = (rgbsum >> 4) & 0x00FF00FF00FF00FFLL; \ rgballsum += _blurGetColors(&pix[yw + (start) * 4]) - 2 * _blurGetColors(&pix[yw + (middle) * 4]) + _blurGetColors(&pix[yw + (end) * 4]); \ rgbsum += rgballsum; \ x++; @@ -222,10 +279,11 @@ x++; int yi = x * 4; #define update(start, middle, end) \ -uint64 res = rgbsum >> 6; \ +uint64 res = rgbsum >> 4; \ pix[yi] = res & 0xFF; \ pix[yi + 1] = (res >> 16) & 0xFF; \ pix[yi + 2] = (res >> 32) & 0xFF; \ +pix[yi + 3] = (res >> 48) & 0xFF; \ rgballsum += rgb[x + (start) * w] - 2 * rgb[x + (middle) * w] + rgb[x + (end) * w]; \ rgbsum += rgballsum; \ y++; \ @@ -250,6 +308,42 @@ yi += stride; return img; } +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); + } + + uchar *pix = img.bits(); + if (pix) { + 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; + 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)); + pix[i + 1] = uchar(g + ((aca * (cg - g)) >> 16)); + pix[i + 2] = uchar(r + ((aca * (cr - r)) >> 16)); + pix[i + 3] = uchar(a + ((aca * (0xFF - a)) >> 16)); + } + } + return img; +} + +QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth) const { + restore(); + loaded(); + + const QPixmap &p(pixData()); + if (p.isNull()) { + return blank()->pix(); + } + if (w <= 0 || !width() || !height() || w == width() && (h <= 0 || h == height())) return p; + if (h <= 0) { + return QPixmap::fromImage(p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly); + } + return QPixmap::fromImage(p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly); +} + QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const { restore(); loaded(); @@ -267,22 +361,36 @@ QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const { return QPixmap::fromImage(img, Qt::ColorOnly); } -QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth) const { +QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool smooth) const { restore(); loaded(); const QPixmap &p(pixData()); if (p.isNull()) { - if (this == blank()) { - int a = 0; - } return blank()->pix(); } - if (w <= 0 || !width() || !height() || w == width()) return p; + if (w <= 0 || !width() || !height() || w == width() && (h <= 0 || h == height())) return QPixmap::fromImage(imageColored(add, p.toImage())); if (h <= 0) { - return QPixmap::fromImage(p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly); + return QPixmap::fromImage(imageColored(add, p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation)), Qt::ColorOnly); } - return QPixmap::fromImage(p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly); + return QPixmap::fromImage(imageColored(add, p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation)), Qt::ColorOnly); +} + +QPixmap Image::pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h) const { + restore(); + loaded(); + + const QPixmap &p(pixData()); + if (p.isNull()) return blank()->pix(); + + QImage img = imageBlur(p.toImage()); + if (h <= 0) { + img = img.scaledToWidth(w, Qt::SmoothTransformation); + } else { + img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + + return QPixmap::fromImage(imageColored(add, img), Qt::ColorOnly); } void Image::forget() const { diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index daaac895f..eca31ac29 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -34,10 +34,14 @@ public: } const QPixmap &pix(int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const; + const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const; + const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const; const QPixmap &pixSingle(int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurredSingle(int32 w = 0, int32 h = 0) const; QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const; QPixmap pixBlurredNoCache(int32 w, int32 h = 0) 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; virtual int32 width() const = 0; virtual int32 height() const = 0; diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index f0346f329..55131a368 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -336,6 +336,26 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink) return (result < end && *result == TextCommand) ? (result + 1) : from; } +QString textEmojiString(EmojiPtr emoji) { + QString result; + result.reserve(emoji->len + (emoji->postfix ? 1 : 0)); + switch (emoji->len) { + case 1: result.append(QChar(emoji->code & 0xFFFF)); break; + case 2: + result.append(QChar((emoji->code >> 16) & 0xFFFF)); + result.append(QChar(emoji->code & 0xFFFF)); + break; + case 4: + result.append(QChar((emoji->code >> 16) & 0xFFFF)); + result.append(QChar(emoji->code & 0xFFFF)); + result.append(QChar((emoji->code2 >> 16) & 0xFFFF)); + result.append(QChar(emoji->code2 & 0xFFFF)); + break; + } + if (emoji->postfix) result.append(QChar(emoji->postfix)); + return result; +} + class TextParser { public: diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index 6b8ac28a2..81f7bb8d7 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -470,3 +470,5 @@ QString textcmdLink(const QString &url, const QString &text); QString textcmdStartColor(const style::color &color); QString textcmdStopColor(); const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true); + +QString textEmojiString(EmojiPtr emoji); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 190f981b5..53460cc5b 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2147,12 +2147,14 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting) { } HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, int32 width) : HistoryMedia(width) +, pixw(1), pixh(1) , data(App::feedPhoto(photo)) , openl(new PhotoLink(data)) { init(); } HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia(width) +, pixw(1), pixh(1) , data(App::feedPhoto(photo)) , openl(new PhotoLink(data, chat)) { init(); @@ -2174,18 +2176,13 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { } else { thumbh = w; // square chat photo updates } - _maxw = w; - _height = _minh = thumbh; - if (_maxw < st::minPhotoWidth) { - _maxw = st::minPhotoWidth; - } - if (_height < st::minPhotoHeight) { - _height = st::minPhotoHeight; - } + _maxw = qMax(w, int32(st::minPhotoSize)); + _minh = qMax(thumbh, int32(st::minPhotoSize)); + _height = resize(w, true, parent); } int32 HistoryPhoto::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { - w = qMin(width, _maxw); + pixw = qMin(width, _maxw); int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height()); if (tw > st::maxMediaSize) { @@ -2196,22 +2193,20 @@ int32 HistoryPhoto::resize(int32 width, bool dontRecountText, const HistoryItem tw = (st::maxMediaSize * tw) / th; th = st::maxMediaSize; } - _height = th; - if (tw > w) { - _height = (w * _height / tw); + pixh = th; + if (tw > pixw) { + pixh = (pixw * pixh / tw); } else { - w = tw; + pixw = tw; } - if (_height > width) { - w = (w * width) / _height; - _height = width; - } - if (w < st::minPhotoWidth) { - w = st::minPhotoWidth; - } - if (_height < st::minPhotoHeight) { - _height = st::minPhotoHeight; + if (pixh > width) { + pixw = (pixw * width) / pixh; + pixh = width; } + if (pixw < 1) pixw = 1; + if (pixh < 1) pixh = 1; + w = qMax(pixw, int16(st::minPhotoSize)); + _height = qMax(pixh, int16(st::minPhotoSize)); return _height; } @@ -2281,16 +2276,14 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i bool full = data->full->loaded(); QPixmap pix; if (full) { - pix = data->full->pixSingle(width); + pix = data->full->pixSingle(pixw, pixh); } else { - pix = data->thumb->pixBlurredSingle(width); + pix = data->thumb->pixBlurredSingle(pixw, pixh); } - if (pix.height() >= _height * cIntRetinaFactor()) { - p.drawPixmap(QPoint(0, 0), pix, QRect(0, (pix.height() - _height * cIntRetinaFactor()) / 2, width * cIntRetinaFactor(), _height * cIntRetinaFactor())); - } else { - int32 usewidth = (width * pix.height()) / (_height * cIntRetinaFactor()); - p.drawPixmap(QRect(0, 0, width, _height), pix, QRect((width - usewidth) * cIntRetinaFactor() / 2, 0, usewidth * cIntRetinaFactor(), pix.height())); + if (pixw < width || pixh < _height) { + p.fillRect(QRect(0, 0, width, _height), st::black->b); } + p.drawPixmap(QPoint((width - pixw) / 2, (_height - pixh) / 2), pix); if (!full) { uint64 dt = itemAnimations().animate(parent, getms()); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); @@ -2330,6 +2323,9 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i int32 dateH = _height - dateY - st::msgDateImgDelta; p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); + if (selected) { + p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); + } p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); @@ -2662,7 +2658,7 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i } p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img); if (selected) { - p.fillRect(st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); + p.fillRect(st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay->b); } int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); @@ -2844,7 +2840,7 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected if (width >= animated.w) { p.drawPixmap(0, 0, animated.frames[animated.frame]); if (selected) { - p.fillRect(0, 0, animated.w, animated.h, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); + p.fillRect(0, 0, animated.w, animated.h, textstyleCurrent()->selectOverlay->b); } } else { bool s = p.renderHints().testFlag(QPainter::SmoothPixmapTransform); @@ -2854,7 +2850,7 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected p.drawPixmap(QRect(0, 0, width, h), animated.frames[animated.frame]); if (!s) p.setRenderHint(QPainter::SmoothPixmapTransform, false); if (selected) { - p.fillRect(0, 0, width, h, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); + p.fillRect(0, 0, width, h, textstyleCurrent()->selectOverlay->b); } } return; @@ -2898,7 +2894,7 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg)); } if (selected) { - p.fillRect(st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); + p.fillRect(st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay->b); } int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); @@ -3058,22 +3054,38 @@ HistoryMedia *HistoryDocument::clone() const { } HistorySticker::HistorySticker(DocumentData *document, int32 width) : HistoryMedia(width) -, data(document) +, pixw(1), pixh(1), data(document) { data->thumb->load(); + updateStickerEmoji(); +} + +bool HistorySticker::updateStickerEmoji() { + const EmojiStickersMap &stickers(cEmojiStickers()); + EmojiStickersMap::const_iterator i = stickers.constFind(data); + QString emoji = (i == stickers.cend()) ? QString() : textEmojiString(i.value()); + if (emoji != _emoji) { + _emoji = emoji; + return true; + } + return false; } void HistorySticker::initDimensions(const HistoryItem *parent) { - _maxw = data->dimensions.width(); - _minh = data->dimensions.height(); - if (_maxw > st::msgMinWidth) { - _minh = (st::msgMinWidth * _minh) / _maxw; - _maxw = st::msgMinWidth; + pixw = data->dimensions.width(); + pixh = data->dimensions.height(); + if (pixw > st::maxStickerSize) { + pixh = (st::maxStickerSize * pixh) / pixw; + pixw = st::maxStickerSize; } - if (_minh > st::maxMediaSize) { - _maxw = (st::maxMediaSize * _maxw) / _minh; - _minh = st::maxMediaSize; + if (pixh > st::maxStickerSize) { + pixw = (st::maxStickerSize * pixw) / pixh; + pixh = st::maxStickerSize; } + if (pixw < 1) pixw = 1; + if (pixh < 1) pixh = 1; + _maxw = qMax(pixw, int16(st::minPhotoSize)); + _minh = qMax(pixh, int16(st::minPhotoSize)); _height = resize(w, true, parent); } @@ -3093,13 +3105,18 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected, data->sticker = ImagePtr(data->data); } } - if (data->sticker->isNull()) { - p.drawPixmap(0, 0, data->thumb->pix(_maxw)); - } else { - p.drawPixmap(0, 0, data->sticker->pix(_maxw)); - } if (selected) { - p.fillRect(0, 0, _maxw, _minh, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); + if (data->sticker->isNull()) { + p.drawPixmap(QPoint((_maxw - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurredColored(textstyleCurrent()->selectOverlay, pixw, pixh)); + } else { + p.drawPixmap(QPoint((_maxw - pixw) / 2, (_minh - pixh) / 2), data->sticker->pixColored(textstyleCurrent()->selectOverlay, pixw, pixh)); + } + } else { + if (data->sticker->isNull()) { + p.drawPixmap(QPoint((_maxw - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurred(pixw, pixh)); + } else { + p.drawPixmap(QPoint((_maxw - pixw) / 2, (_minh - pixh) / 2), data->sticker->pix(pixw, pixh)); + } } // date @@ -3114,6 +3131,9 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 dateH = _height - dateY - st::msgDateImgDelta; p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); + if (selected) { + p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); + } p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); @@ -3155,11 +3175,11 @@ int32 HistorySticker::resize(int32 width, bool dontRecountText, const HistoryIte } const QString HistorySticker::inDialogsText() const { - return lang(lng_in_dlg_sticker); + return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji); } const QString HistorySticker::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_sticker) + qsl(" ]"); + return qsl("[ ") + inDialogsText() + qsl(" ]"); } bool HistorySticker::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { @@ -3715,7 +3735,7 @@ int32 HistoryImageLink::fullWidth() const { case GoogleMapsLink: return st::locationSize.width(); } } - return st::minPhotoWidth; + return st::minPhotoSize; } int32 HistoryImageLink::fullHeight() const { @@ -3727,22 +3747,22 @@ int32 HistoryImageLink::fullHeight() const { case GoogleMapsLink: return st::locationSize.height(); } } - return st::minPhotoHeight; + return st::minPhotoSize; } void HistoryImageLink::initDimensions(const HistoryItem *parent) { int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight()); - int32 thumbw = qMax(tw, int32(st::minPhotoWidth)), maxthumbh = thumbw; + int32 thumbw = qMax(tw, int32(st::minPhotoSize)), maxthumbh = thumbw; int32 thumbh = qRound(th * float64(thumbw) / tw); if (thumbh > maxthumbh) { thumbw = qRound(thumbw * float64(maxthumbh) / thumbh); thumbh = maxthumbh; - if (thumbw < st::minPhotoWidth) { - thumbw = st::minPhotoWidth; + if (thumbw < st::minPhotoSize) { + thumbw = st::minPhotoSize; } } - if (thumbh < st::minPhotoHeight) { - thumbh = st::minPhotoHeight; + if (thumbh < st::minPhotoSize) { + thumbh = st::minPhotoSize; } if (!w) { w = thumbw; @@ -3812,6 +3832,9 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte int32 dateH = _height - dateY - st::msgDateImgDelta; p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); + if (selected) { + p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); + } p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); @@ -3849,11 +3872,11 @@ int32 HistoryImageLink::resize(int32 width, bool dontRecountText, const HistoryI w = (w * width) / _height; _height = width; } - if (w < st::minPhotoWidth) { - w = st::minPhotoWidth; + if (w < st::minPhotoSize) { + w = st::minPhotoSize; } - if (_height < st::minPhotoHeight) { - _height = st::minPhotoHeight; + if (_height < st::minPhotoSize) { + _height = st::minPhotoSize; } return _height; } @@ -4000,7 +4023,7 @@ void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tTex } void HistoryMessage::initMediaFromDocument(DocumentData *doc) { - if (doc->type == StickerDocument && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->size < StickerInMemory) { + if (doc->type == StickerDocument && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->dimensions.width() <= StickerMaxSize && doc->dimensions.height() <= StickerMaxSize && doc->size < StickerInMemory) { _media = new HistorySticker(doc); } else { _media = new HistoryDocument(doc); @@ -4318,6 +4341,15 @@ QString HistoryMessage::notificationText() const { return msg; } +void HistoryMessage::updateStickerEmoji() { + if (_media) { + if (_media->updateStickerEmoji()) { + _history->textCachedFor = 0; + if (App::wnd()) App::wnd()->update(); + } + } +} + HistoryMessage::~HistoryMessage() { if (_media) { _media->unregItem(this); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index a1667b421..691c056d5 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1166,6 +1166,8 @@ public: } virtual void updateMedia(const MTPMessageMedia &media) { } + virtual void updateStickerEmoji() { + } virtual QString selectedText(uint32 selection) const { return qsl("[-]"); @@ -1240,6 +1242,10 @@ public: virtual void updateFrom(const MTPMessageMedia &media) { } + + virtual bool updateStickerEmoji() { + return false; + } virtual bool animating() const { return false; @@ -1291,6 +1297,7 @@ public: } private: + int16 pixw, pixh; PhotoData *data; TextLinkPtr openl; @@ -1432,10 +1439,13 @@ public: void unregItem(HistoryItem *item); void updateFrom(const MTPMessageMedia &media); + bool updateStickerEmoji(); private: + int16 pixw, pixh; DocumentData *data; + QString _emoji; }; @@ -1577,6 +1587,7 @@ public: void updateMedia(const MTPMessageMedia &media) { if (_media) _media->updateFrom(media); } + void updateStickerEmoji(); QString selectedText(uint32 selection) const; HistoryMedia *getMedia(bool inOverview = false) const; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index fa18b0c44..bad49f23d 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1774,9 +1774,11 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { const MTPDmessages_allStickers &d(stickers.c_messages_allStickers()); AllStickers all; + EmojiStickersMap map; const QVector &docs(d.vdocuments.c_vector().v); + QSet found; const RecentStickerPack &recent(cRecentStickers()); RecentStickerPack add; add.reserve(docs.size()); @@ -1786,13 +1788,32 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { if (!doc) continue; int32 j = 0, s = recent.size(); for (; j < s; ++j) { - if (doc == recent.at(j).first) break; + if (doc == recent.at(j).first) { + found.insert(doc); + break; + } } if (j < s) continue; add.push_back(qMakePair(doc, addValue)); } - if (!add.isEmpty()) { - cSetRecentStickers(add + recent); + bool needRemove = false; + for (int32 i = 0, l = recent.size(); i < l; ++i) { + if (recent.at(i).second > 0 && !found.contains(recent.at(i).first)) { + needRemove = true; + break; + } + } + if (!add.isEmpty() || needRemove) { + if (needRemove) { + for (int32 i = 0, l = recent.size(); i < l; ++i) { + if (recent.at(i).second <= 0 || found.contains(recent.at(i).first)) { + add.push_back(recent.at(i)); + } + } + } else { + add += recent; + } + cSetRecentStickers(add); Local::writeRecentStickers(); _emojiPan.onTabChange(); } @@ -1828,7 +1849,9 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { StickerPack &pack(all[e]); pack.reserve(pack.size() + docs.size()); for (int32 j = 0, s = docs.size(); j < s; ++j) { - pack.push_back(App::document(docs.at(j).v)); + DocumentData *doc = App::document(docs.at(j).v); + pack.push_back(doc); + map.insert(doc, e); } } } else { @@ -1839,6 +1862,17 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { cSetStickers(all); cSetStickersHash(qba(d.vhash)); + cSetEmojiStickers(map); + + const DocumentItems &items(App::documentItems()); + for (EmojiStickersMap::const_iterator i = map.cbegin(), e = map.cend(); i != e; ++i) { + DocumentItems::const_iterator j = items.constFind(i.key()); + if (j != items.cend()) { + for (HistoryItemsMap::const_iterator k = j->cbegin(), end = j->cend(); k != end; ++k) { + k.key()->updateStickerEmoji(); + } + } + } // updateStickerPan(); _emojiPan.onTabChange(); @@ -2995,11 +3029,11 @@ void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType ty imageLoader.append(files, histPeer->id, type); } -void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type) { - if (!hist) return; +void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer) { + if (!peer && !hist) return; App::wnd()->activateWindow(); - imageLoader.append(fileContent, histPeer->id, type); + imageLoader.append(fileContent, peer ? peer : histPeer->id, type); } void HistoryWidget::onPhotoReady() { @@ -3089,7 +3123,7 @@ void HistoryWidget::onPhotoUploaded(MsgId newId, const MTPInputFile &file) { uint64 randomId = MTP::nonce(); App::historyRegRandom(randomId, newId); History *hist = item->history(); - hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); + hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), App::main()->rpcFail(&MainWidget::sendPhotoFailed, randomId), 0, 0, hist->sendRequestId); } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 17d58742c..b878e5f6a 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -298,7 +298,7 @@ public: void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText = false); void uploadConfirmImageUncompressed(bool ctrlShiftEnter); void uploadMedias(const QStringList &files, ToPrepareMediaType type); - void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type); + void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer = 0); void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname); void confirmSendImage(const ReadyLocalMedia &img); void cancelSendImage(); diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index a35f387d9..e5b6267cb 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -40,6 +40,10 @@ public: return res; } + void mousePressEvent(QMouseEvent *e) { + e->accept(); + } + signals: void closed(); diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index 99286eab3..46d206717 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -31,12 +31,13 @@ LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLo }; void LocalImageLoaderPrivate::prepareImages() { - QString file, filename, mime; + QString file, filename, mime, stickerMime = qsl("image/webp"); int32 filesize = 0; QImage img; QByteArray data; PeerId peer; - uint64 id, jpeg_id = 0; + uint64 id, thumbId = 0; + QString thumbExt = "jpg"; ToPrepareMediaType type; bool animated = false; bool ctrlShiftEnter = false; @@ -72,12 +73,13 @@ void LocalImageLoaderPrivate::prepareImages() { type = ToPrepareDocument; } } - if (type != ToPrepareAuto && info.size() < MaxUploadPhotoSize) { - img = App::readImage(file, 0, true, &animated); - } if (type == ToPrepareDocument) { mime = mimeTypeForFile(info).name(); } + if (type != ToPrepareAuto && info.size() < MaxUploadPhotoSize) { + bool opaque = (mime != stickerMime); + img = App::readImage(file, 0, opaque, &animated); + } filename = info.fileName(); filesize = info.size(); } else if (!data.isEmpty()) { @@ -95,10 +97,15 @@ void LocalImageLoaderPrivate::prepareImages() { if (type == ToPrepareDocument) { mime = mimeType.name(); } - filename = qsl("Document"); - QStringList patterns = mimeType.globPatterns(); - if (!patterns.isEmpty()) { - filename = patterns.front().replace('*', filename); + if (mime == "image/jpeg") { + filename = filedialogDefaultName(qsl("image"), qsl(".jpg"), QString(), true); + } else { + QString ext; + QStringList patterns = mimeType.globPatterns(); + if (!patterns.isEmpty()) { + ext = patterns.front().replace('*', QString()); + } + filename = filedialogDefaultName(qsl("doc"), ext, QString(), true); } filesize = data.size(); } @@ -113,7 +120,15 @@ void LocalImageLoaderPrivate::prepareImages() { } filesize = data.size(); } else { - type = ToPreparePhoto; // only photo from QImage + if (img.hasAlphaChannel()) { + QImage solid(img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); + solid.fill(st::white->c); + { + QPainter(&solid).drawImage(0, 0, img); + } + img = solid; + } + type = ToPreparePhoto; filename = qsl("Untitled.jpg"); filesize = 0; } @@ -163,29 +178,32 @@ void LocalImageLoaderPrivate::prepareImages() { photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector(photoSizes)); - jpeg_id = id; + thumbId = id; } else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) { int32 w = img.width(), h = img.height(); QByteArray thumbFormat = "JPG"; + int32 thumbQuality = 87; if (animated) { attributes.push_back(MTP_documentAttributeAnimated()); - } else if (mime == qsl("image/webp") && w > 0 && h > 0 && filesize < StickerInMemory) { + } else if (mime == stickerMime && w > 0 && h > 0 && w <= StickerMaxSize && h <= StickerMaxSize && filesize < StickerInMemory) { attributes.push_back(MTP_documentAttributeSticker()); thumbFormat = "webp"; + thumbExt = qsl("webp"); } attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h))); + if (w < 20 * h && h < 20 * w) { + QPixmap full = (w > 90 || h > 90) ? QPixmap::fromImage(img.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img, Qt::ColorOnly); - QPixmap full = (w > 90 || h > 90) ? QPixmap::fromImage(img.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img, Qt::ColorOnly); + { + QBuffer jpegBuffer(&jpeg); + full.save(&jpegBuffer, thumbFormat, thumbQuality); + } - { - QBuffer jpegBuffer(&jpeg); - full.save(&jpegBuffer, thumbFormat, 87); + photoThumbs.insert('0', full); + thumb = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)); + + thumbId = MTP::nonce(); } - - photoThumbs.insert('0', full); - thumb = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)); - - jpeg_id = MTP::nonce(); } if (type == ToPrepareDocument) { @@ -194,7 +212,7 @@ void LocalImageLoaderPrivate::prepareImages() { { QMutexLocker lock(loader->readyMutex()); - loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, jpeg_id, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter)); + loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter)); } { diff --git a/Telegram/SourceFiles/localimageloader.h b/Telegram/SourceFiles/localimageloader.h index 40e31848d..2e77364b9 100644 --- a/Telegram/SourceFiles/localimageloader.h +++ b/Telegram/SourceFiles/localimageloader.h @@ -43,8 +43,8 @@ typedef QList ToPrepareMedias; typedef QMap LocalFileParts; struct ReadyLocalMedia { - ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &jpeg_id, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter) : - type(type), file(file), filename(filename), filesize(filesize), data(data), id(id), jpeg_id(jpeg_id), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) { + ReadyLocalMedia(ToPrepareMediaType 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) : + type(type), file(file), filename(filename), filesize(filesize), data(data), id(id), thumbId(thumbId), thumbExt(thumbExt), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) { if (!jpeg.isEmpty()) { int32 size = jpeg.size(); for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) { @@ -58,7 +58,8 @@ struct ReadyLocalMedia { QString file, filename; int32 filesize; QByteArray data; - uint64 id, jpeg_id; // id always file-id of media, jpeg_id is file-id of thumb ( == id for photos) + QString thumbExt; + uint64 id, thumbId; // id always file-id of media, thumbId is file-id of thumb ( == id for photos) PeerId peer; MTPPhoto photo; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 15bb5a4b7..998e62be5 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -664,6 +664,52 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu } } +bool MainWidget::sendPhotoFailed(uint64 randomId, const RPCError &e) { + if (e.type() == qsl("PHOTO_INVALID_DIMENSIONS")) { + if (_resendImgRandomIds.isEmpty()) { + ConfirmBox *box = new ConfirmBox(lang(lng_bad_image_for_photo)); + connect(box, SIGNAL(confirmed()), this, SLOT(onResendAsDocument())); + connect(box, SIGNAL(cancelled()), this, SLOT(onCancelResend())); + connect(box, SIGNAL(destroyed()), this, SLOT(onCancelResend())); + App::wnd()->showLayer(box); + } + _resendImgRandomIds.push_back(randomId); + return true; + } + return false; +} + +void MainWidget::onResendAsDocument() { + QList tmp = _resendImgRandomIds; + _resendImgRandomIds.clear(); + for (int32 i = 0, l = tmp.size(); i < l; ++i) { + if (HistoryItem *item = App::histItemById(App::histItemByRandom(tmp.at(i)))) { + if (HistoryPhoto *media = dynamic_cast(item->getMedia())) { + PhotoData *photo = media->photo(); + if (!photo->full->isNull()) { + photo->full->forget(); + QByteArray data = photo->full->savedData(); + if (!data.isEmpty()) { + history.uploadMedia(data, ToPrepareDocument, item->history()->peer->id); + } + } + } + item->destroy(); + } + } + App::wnd()->layerHidden(); +} + +void MainWidget::onCancelResend() { + QList tmp = _resendImgRandomIds; + _resendImgRandomIds.clear(); + for (int32 i = 0, l = tmp.size(); i < l; ++i) { + if (HistoryItem *item = App::histItemById(App::histItemByRandom(tmp.at(i)))) { + item->destroy(); + } + } +} + void MainWidget::forwardSelectedItems() { if (overview) { overview->onForwardSelected(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 8620f0888..24738f0c7 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -277,6 +277,8 @@ public: void checkPeerHistory(PeerData *peer); void checkedHistory(PeerData *peer, const MTPmessages_Messages &result); + bool sendPhotoFailed(uint64 randomId, const RPCError &e); + void forwardSelectedItems(); void deleteSelectedItems(); void clearSelectedItems(); @@ -359,6 +361,9 @@ public slots: void onForwardCancel(QObject *obj = 0); + void onResendAsDocument(); + void onCancelResend(); + private: void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result); @@ -370,6 +375,8 @@ private: QString failedFileName; void loadFailed(mtpFileLoader *loader, bool started, const char *retrySlot); + QList _resendImgRandomIds; + void gotDifference(const MTPupdates_Difference &diff); bool failDifference(const RPCError &e); void feedDifference(const MTPVector &users, const MTPVector &chats, const MTPVector &msgs, const MTPVector &other); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index c7e1500c6..2cec1cb54 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -74,6 +74,9 @@ RecentEmojiPreload gRecentEmojisPreload; AllStickers gStickers; QByteArray gStickersHash; + +EmojiStickersMap gEmojiStickers; + RecentStickerPack gRecentStickers; int32 gLang = -2; // auto diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 4c769b8c2..94ee184f3 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -150,6 +150,10 @@ typedef QVector StickerPack; typedef QMap AllStickers; DeclareSetting(AllStickers, Stickers); DeclareSetting(QByteArray, StickersHash); + +typedef QMap EmojiStickersMap; +DeclareSetting(EmojiStickersMap, EmojiStickers); + typedef QList > RecentStickerPack; DeclareSetting(RecentStickerPack, RecentStickers); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index 7e181e153..2c365711a 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -944,7 +944,7 @@ void SettingsInner::onChangeLanguage() { connect(box, SIGNAL(confirmed()), this, SLOT(onSaveTestLang())); App::wnd()->showLayer(box); } else { - App::wnd()->showLayer(new ConfirmBox("Custom lang failed :(\n\nError: " + cLangErrors(), true, lang(lng_close))); + App::wnd()->showLayer(new ConfirmBox("Custom lang failed :(\n\nError: " + loader.errors(), true, lang(lng_close))); } } } else {