This commit is contained in:
John Preston 2015-01-05 23:19:11 +03:00
commit 1f77e22ed2
27 changed files with 442 additions and 146 deletions

View File

@ -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_name" = "Please enter your first and last name.";
"lng_bad_photo" = "Bad image selected."; "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_title" = "Information and photo";
"lng_signup_desc" = "Please enter your name and\nupload a 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_audio" = "Audio";
"lng_in_dlg_document" = "Document"; "lng_in_dlg_document" = "Document";
"lng_in_dlg_sticker" = "Sticker"; "lng_in_dlg_sticker" = "Sticker";
"lng_in_dlg_sticker_emoji" = "{emoji} (sticker)";
"lng_send_button" = "Send"; "lng_send_button" = "Send";
"lng_message_ph" = "Write a message.."; "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_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_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 // Mac specific

View File

@ -1627,7 +1627,7 @@ btnContext: iconedButton(btnDefIconed) {
opacity: 1; opacity: 1;
overOpacity: 1; overOpacity: 1;
width: 172px; width: -32px;
height: 36px; height: 36px;
color: black; color: black;
@ -1655,9 +1655,9 @@ mediaviewLoader: size(78px, 33px);
mediaviewLoaderPoint: size(9px, 9px); mediaviewLoaderPoint: size(9px, 9px);
mediaviewLoaderSkip: 9px; mediaviewLoaderSkip: 9px;
minPhotoWidth: 90px; minPhotoSize: 90px;
minPhotoHeight: 90px;
maxMediaSize: 420px; maxMediaSize: 420px;
maxStickerSize: 256px;
usernameFont: font(14px); usernameFont: font(14px);
usernameColor: #777; usernameColor: #777;

View File

@ -1292,6 +1292,8 @@ namespace App {
documentsData.clear(); documentsData.clear();
cSetRecentStickers(RecentStickerPack()); cSetRecentStickers(RecentStickerPack());
cSetStickersHash(QByteArray()); cSetStickersHash(QByteArray());
cSetStickers(AllStickers());
cSetEmojiStickers(EmojiStickersMap());
::videoItems.clear(); ::videoItems.clear();
::audioItems.clear(); ::audioItems.clear();
::documentItems.clear(); ::documentItems.clear();
@ -2013,7 +2015,7 @@ namespace App {
} }
exif_data_free(exifData); exif_data_free(exifData);
} }
} else if (opaque) { } else if (opaque && result.hasAlphaChannel()) {
QImage solid(result.width(), result.height(), QImage::Format_ARGB32_Premultiplied); QImage solid(result.width(), result.height(), QImage::Format_ARGB32_Premultiplied);
solid.fill(st::white->c); solid.fill(st::white->c);
{ {

View File

@ -481,7 +481,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
int32 filesize = 0; int32 filesize = 0;
QByteArray data; 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); connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);

View File

@ -89,6 +89,7 @@ void ConfirmBox::mousePressEvent(QMouseEvent *e) {
textlnkDown(textlnkOver()); textlnkDown(textlnkOver());
update(); update();
} }
return LayeredWidget::mousePressEvent(e);
} }
void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) { void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {

View File

@ -57,7 +57,7 @@ PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : _downState(0
} }
void PhotoCropBox::mousePressEvent(QMouseEvent *e) { void PhotoCropBox::mousePressEvent(QMouseEvent *e) {
if (e->button() != Qt::LeftButton) return; if (e->button() != Qt::LeftButton) return LayeredWidget::mousePressEvent(e);
_downState = mouseState(e->pos()); _downState = mouseState(e->pos());
_fromposx = e->pos().x(); _fromposx = e->pos().x();
@ -65,6 +65,8 @@ void PhotoCropBox::mousePressEvent(QMouseEvent *e) {
_fromcropx = _cropx; _fromcropx = _cropx;
_fromcropy = _cropy; _fromcropy = _cropy;
_fromcropw = _cropw; _fromcropw = _cropw;
return LayeredWidget::mousePressEvent(e);
} }
int32 PhotoCropBox::mouseState(QPoint p) { int32 PhotoCropBox::mouseState(QPoint p) {

View File

@ -86,7 +86,9 @@ enum {
AudioVoiceMsgChannels = 2, // stereo AudioVoiceMsgChannels = 2, // stereo
AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers
AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded 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 MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app
MaxZoomLevel = 7, // x8 MaxZoomLevel = 7, // x8

View File

@ -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())); 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()); 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); QPoint ppos = pos + QPoint((stickerSize - w) / 2, (stickerSize - h) / 2);
if (sticker->sticker->isNull()) { if (sticker->sticker->isNull()) {
p.drawPixmap(ppos, sticker->thumb->pix(w)); p.drawPixmap(ppos, sticker->thumb->pix(w, h));
} else { } else {
p.drawPixmap(ppos, sticker->sticker->pix(w)); p.drawPixmap(ppos, sticker->sticker->pix(w, h));
} }
if (hover > 0 && _isUserGen[index]) { if (hover > 0 && _isUserGen[index]) {

View File

@ -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)); 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) { 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 { } else {
emit documentReady(uploading, doc); emit documentReady(uploading, doc);
} }
@ -166,7 +166,7 @@ void FileUploader::sendNext() {
} else { } else {
LocalFileParts::iterator part = i->media.parts.begin(); 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()); requestsSent.insert(requestId, part.value());
dcMap.insert(requestId, todc); dcMap.insert(requestId, todc);
sentSize += part.value().size(); sentSize += part.value().size();

View File

@ -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))); connect(b, SIGNAL(stateChanged(int,ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int,ButtonStateChangeSource)));
_width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + b->width())); _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(); _height += b->height();
resize(_width, _height); resize(_width, _height);
@ -61,6 +62,8 @@ ContextMenu::Actions &ContextMenu::actions() {
void ContextMenu::actionChanged() { void ContextMenu::actionChanged() {
for (int32 i = 0, l = _actions.size(); i < l; ++i) { for (int32 i = 0, l = _actions.size(); i < l; ++i) {
_buttons[i]->setText(_actions[i]->text()); _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());
} }
} }

View File

@ -297,21 +297,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
uint32 index = imageName.mid(8).toUInt(0, 16); uint32 index = imageName.mid(8).toUInt(0, 16);
const EmojiData *emoji = getEmoji(index); const EmojiData *emoji = getEmoji(index);
if (emoji) { if (emoji) {
emojiText.reserve(emoji->len + (emoji->postfix ? 1 : 0)); emojiText = textEmojiString(emoji);
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));
} }
} }
} }

View File

@ -79,7 +79,7 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
w *= cIntRetinaFactor(); w *= cIntRetinaFactor();
h *= 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); Sizes::const_iterator i = _sizesCache.constFind(k);
if (i == _sizesCache.cend()) { if (i == _sizesCache.cend()) {
QPixmap p(pixBlurredNoCache(w, h)); QPixmap p(pixBlurredNoCache(w, h));
@ -92,6 +92,52 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
return i.value(); 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 { const QPixmap &Image::pixSingle(int32 w, int32 h) const {
restore(); restore();
checkload(); checkload();
@ -128,7 +174,7 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
w *= cIntRetinaFactor(); w *= cIntRetinaFactor();
h *= cIntRetinaFactor(); h *= cIntRetinaFactor();
} }
uint64 k = 0x8000000000000000LL | 0LL; uint64 k = 0x1000000000000000LL | 0LL;
Sizes::const_iterator i = _sizesCache.constFind(k); Sizes::const_iterator i = _sizesCache.constFind(k);
if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) { if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) {
if (i != _sizesCache.cend()) { if (i != _sizesCache.cend()) {
@ -146,29 +192,40 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
namespace { namespace {
static inline uint64 _blurGetColors(const uchar *p) { 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 imageBlur(QImage img) {
QImage::Format fmt = img.format(); QImage::Format fmt = img.format();
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
QImage tmp(img.width(), img.height(), QImage::Format_ARGB32); img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
{
QPainter p(&tmp);
p.drawImage(0, 0, img);
}
img = tmp;
} }
uchar *pix = img.bits(); uchar *pix = img.bits();
if (pix) { if (pix) {
const int w = img.width(), h = img.height(), stride = w * 4; int w = img.width(), h = img.height(), wold = w, hold = h;
const int radius = 7; const int radius = 3;
const int r1 = radius + 1; const int r1 = radius + 1;
const int div = radius * 2 + 1; const int div = radius * 2 + 1;
const int stride = w * 4;
if (radius < 16 && div < w && div < h && 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]; uint64 *rgb = new uint64[w * h];
int x, y, i; int x, y, i;
@ -189,7 +246,7 @@ QImage imageBlur(QImage img) {
x = 0; x = 0;
#define update(start, middle, end) \ #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]); \ rgballsum += _blurGetColors(&pix[yw + (start) * 4]) - 2 * _blurGetColors(&pix[yw + (middle) * 4]) + _blurGetColors(&pix[yw + (end) * 4]); \
rgbsum += rgballsum; \ rgbsum += rgballsum; \
x++; x++;
@ -222,10 +279,11 @@ x++;
int yi = x * 4; int yi = x * 4;
#define update(start, middle, end) \ #define update(start, middle, end) \
uint64 res = rgbsum >> 6; \ uint64 res = rgbsum >> 4; \
pix[yi] = res & 0xFF; \ pix[yi] = res & 0xFF; \
pix[yi + 1] = (res >> 16) & 0xFF; \ pix[yi + 1] = (res >> 16) & 0xFF; \
pix[yi + 2] = (res >> 32) & 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]; \ rgballsum += rgb[x + (start) * w] - 2 * rgb[x + (middle) * w] + rgb[x + (end) * w]; \
rgbsum += rgballsum; \ rgbsum += rgballsum; \
y++; \ y++; \
@ -250,6 +308,42 @@ yi += stride;
return img; 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 { QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const {
restore(); restore();
loaded(); loaded();
@ -267,22 +361,36 @@ QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const {
return QPixmap::fromImage(img, Qt::ColorOnly); 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(); restore();
loaded(); loaded();
const QPixmap &p(pixData()); const QPixmap &p(pixData());
if (p.isNull()) { if (p.isNull()) {
if (this == blank()) {
int a = 0;
}
return blank()->pix(); 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) { 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 { void Image::forget() const {

View File

@ -34,10 +34,14 @@ public:
} }
const QPixmap &pix(int32 w = 0, int32 h = 0) const; const QPixmap &pix(int32 w = 0, int32 h = 0) const;
const QPixmap &pixBlurred(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 &pixSingle(int32 w = 0, int32 h = 0) const;
const QPixmap &pixBlurredSingle(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 pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const;
QPixmap pixBlurredNoCache(int32 w, int32 h = 0) 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 width() const = 0;
virtual int32 height() const = 0; virtual int32 height() const = 0;

View File

@ -336,6 +336,26 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink)
return (result < end && *result == TextCommand) ? (result + 1) : from; 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 { class TextParser {
public: public:

View File

@ -470,3 +470,5 @@ QString textcmdLink(const QString &url, const QString &text);
QString textcmdStartColor(const style::color &color); QString textcmdStartColor(const style::color &color);
QString textcmdStopColor(); QString textcmdStopColor();
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true); const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);
QString textEmojiString(EmojiPtr emoji);

View File

@ -2147,12 +2147,14 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting) {
} }
HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, int32 width) : HistoryMedia(width) HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, int32 width) : HistoryMedia(width)
, pixw(1), pixh(1)
, data(App::feedPhoto(photo)) , data(App::feedPhoto(photo))
, openl(new PhotoLink(data)) { , openl(new PhotoLink(data)) {
init(); init();
} }
HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia(width) HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia(width)
, pixw(1), pixh(1)
, data(App::feedPhoto(photo)) , data(App::feedPhoto(photo))
, openl(new PhotoLink(data, chat)) { , openl(new PhotoLink(data, chat)) {
init(); init();
@ -2174,18 +2176,13 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) {
} else { } else {
thumbh = w; // square chat photo updates thumbh = w; // square chat photo updates
} }
_maxw = w; _maxw = qMax(w, int32(st::minPhotoSize));
_height = _minh = thumbh; _minh = qMax(thumbh, int32(st::minPhotoSize));
if (_maxw < st::minPhotoWidth) { _height = resize(w, true, parent);
_maxw = st::minPhotoWidth;
}
if (_height < st::minPhotoHeight) {
_height = st::minPhotoHeight;
}
} }
int32 HistoryPhoto::resize(int32 width, bool dontRecountText, const HistoryItem *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()); int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height());
if (tw > st::maxMediaSize) { if (tw > st::maxMediaSize) {
@ -2196,22 +2193,20 @@ int32 HistoryPhoto::resize(int32 width, bool dontRecountText, const HistoryItem
tw = (st::maxMediaSize * tw) / th; tw = (st::maxMediaSize * tw) / th;
th = st::maxMediaSize; th = st::maxMediaSize;
} }
_height = th; pixh = th;
if (tw > w) { if (tw > pixw) {
_height = (w * _height / tw); pixh = (pixw * pixh / tw);
} else { } else {
w = tw; pixw = tw;
} }
if (_height > width) { if (pixh > width) {
w = (w * width) / _height; pixw = (pixw * width) / pixh;
_height = width; pixh = width;
}
if (w < st::minPhotoWidth) {
w = st::minPhotoWidth;
}
if (_height < st::minPhotoHeight) {
_height = st::minPhotoHeight;
} }
if (pixw < 1) pixw = 1;
if (pixh < 1) pixh = 1;
w = qMax(pixw, int16(st::minPhotoSize));
_height = qMax(pixh, int16(st::minPhotoSize));
return _height; return _height;
} }
@ -2281,16 +2276,14 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i
bool full = data->full->loaded(); bool full = data->full->loaded();
QPixmap pix; QPixmap pix;
if (full) { if (full) {
pix = data->full->pixSingle(width); pix = data->full->pixSingle(pixw, pixh);
} else { } else {
pix = data->thumb->pixBlurredSingle(width); pix = data->thumb->pixBlurredSingle(pixw, pixh);
} }
if (pix.height() >= _height * cIntRetinaFactor()) { if (pixw < width || pixh < _height) {
p.drawPixmap(QPoint(0, 0), pix, QRect(0, (pix.height() - _height * cIntRetinaFactor()) / 2, width * cIntRetinaFactor(), _height * cIntRetinaFactor())); p.fillRect(QRect(0, 0, width, _height), st::black->b);
} 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()));
} }
p.drawPixmap(QPoint((width - pixw) / 2, (_height - pixh) / 2), pix);
if (!full) { if (!full) {
uint64 dt = itemAnimations().animate(parent, getms()); uint64 dt = itemAnimations().animate(parent, getms());
int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); 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; int32 dateH = _height - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); 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.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p); p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); 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); p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img);
if (selected) { 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(); 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) { if (width >= animated.w) {
p.drawPixmap(0, 0, animated.frames[animated.frame]); p.drawPixmap(0, 0, animated.frames[animated.frame]);
if (selected) { 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 { } else {
bool s = p.renderHints().testFlag(QPainter::SmoothPixmapTransform); 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]); p.drawPixmap(QRect(0, 0, width, h), animated.frames[animated.frame]);
if (!s) p.setRenderHint(QPainter::SmoothPixmapTransform, false); if (!s) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
if (selected) { if (selected) {
p.fillRect(0, 0, width, h, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); p.fillRect(0, 0, width, h, textstyleCurrent()->selectOverlay->b);
} }
} }
return; 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)); p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg));
} }
if (selected) { 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(); 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) HistorySticker::HistorySticker(DocumentData *document, int32 width) : HistoryMedia(width)
, data(document) , pixw(1), pixh(1), data(document)
{ {
data->thumb->load(); 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) { void HistorySticker::initDimensions(const HistoryItem *parent) {
_maxw = data->dimensions.width(); pixw = data->dimensions.width();
_minh = data->dimensions.height(); pixh = data->dimensions.height();
if (_maxw > st::msgMinWidth) { if (pixw > st::maxStickerSize) {
_minh = (st::msgMinWidth * _minh) / _maxw; pixh = (st::maxStickerSize * pixh) / pixw;
_maxw = st::msgMinWidth; pixw = st::maxStickerSize;
} }
if (_minh > st::maxMediaSize) { if (pixh > st::maxStickerSize) {
_maxw = (st::maxMediaSize * _maxw) / _minh; pixw = (st::maxStickerSize * pixw) / pixh;
_minh = st::maxMediaSize; 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); _height = resize(w, true, parent);
} }
@ -3093,13 +3105,18 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
data->sticker = ImagePtr(data->data); 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) { 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 // date
@ -3114,6 +3131,9 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
int32 dateH = _height - dateY - st::msgDateImgDelta; int32 dateH = _height - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); 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.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p); p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); 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 { 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 { 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 { 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(); case GoogleMapsLink: return st::locationSize.width();
} }
} }
return st::minPhotoWidth; return st::minPhotoSize;
} }
int32 HistoryImageLink::fullHeight() const { int32 HistoryImageLink::fullHeight() const {
@ -3727,22 +3747,22 @@ int32 HistoryImageLink::fullHeight() const {
case GoogleMapsLink: return st::locationSize.height(); case GoogleMapsLink: return st::locationSize.height();
} }
} }
return st::minPhotoHeight; return st::minPhotoSize;
} }
void HistoryImageLink::initDimensions(const HistoryItem *parent) { void HistoryImageLink::initDimensions(const HistoryItem *parent) {
int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight()); 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); int32 thumbh = qRound(th * float64(thumbw) / tw);
if (thumbh > maxthumbh) { if (thumbh > maxthumbh) {
thumbw = qRound(thumbw * float64(maxthumbh) / thumbh); thumbw = qRound(thumbw * float64(maxthumbh) / thumbh);
thumbh = maxthumbh; thumbh = maxthumbh;
if (thumbw < st::minPhotoWidth) { if (thumbw < st::minPhotoSize) {
thumbw = st::minPhotoWidth; thumbw = st::minPhotoSize;
} }
} }
if (thumbh < st::minPhotoHeight) { if (thumbh < st::minPhotoSize) {
thumbh = st::minPhotoHeight; thumbh = st::minPhotoSize;
} }
if (!w) { if (!w) {
w = thumbw; w = thumbw;
@ -3812,6 +3832,9 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte
int32 dateH = _height - dateY - st::msgDateImgDelta; int32 dateH = _height - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); 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.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p); p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); 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; w = (w * width) / _height;
_height = width; _height = width;
} }
if (w < st::minPhotoWidth) { if (w < st::minPhotoSize) {
w = st::minPhotoWidth; w = st::minPhotoSize;
} }
if (_height < st::minPhotoHeight) { if (_height < st::minPhotoSize) {
_height = st::minPhotoHeight; _height = st::minPhotoSize;
} }
return _height; return _height;
} }
@ -4000,7 +4023,7 @@ void HistoryMessage::initMedia(const MTPMessageMedia &media, QString &currentTex
} }
void HistoryMessage::initMediaFromDocument(DocumentData *doc) { 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); _media = new HistorySticker(doc);
} else { } else {
_media = new HistoryDocument(doc); _media = new HistoryDocument(doc);
@ -4318,6 +4341,15 @@ QString HistoryMessage::notificationText() const {
return msg; return msg;
} }
void HistoryMessage::updateStickerEmoji() {
if (_media) {
if (_media->updateStickerEmoji()) {
_history->textCachedFor = 0;
if (App::wnd()) App::wnd()->update();
}
}
}
HistoryMessage::~HistoryMessage() { HistoryMessage::~HistoryMessage() {
if (_media) { if (_media) {
_media->unregItem(this); _media->unregItem(this);

View File

@ -1166,6 +1166,8 @@ public:
} }
virtual void updateMedia(const MTPMessageMedia &media) { virtual void updateMedia(const MTPMessageMedia &media) {
} }
virtual void updateStickerEmoji() {
}
virtual QString selectedText(uint32 selection) const { virtual QString selectedText(uint32 selection) const {
return qsl("[-]"); return qsl("[-]");
@ -1240,6 +1242,10 @@ public:
virtual void updateFrom(const MTPMessageMedia &media) { virtual void updateFrom(const MTPMessageMedia &media) {
} }
virtual bool updateStickerEmoji() {
return false;
}
virtual bool animating() const { virtual bool animating() const {
return false; return false;
@ -1291,6 +1297,7 @@ public:
} }
private: private:
int16 pixw, pixh;
PhotoData *data; PhotoData *data;
TextLinkPtr openl; TextLinkPtr openl;
@ -1432,10 +1439,13 @@ public:
void unregItem(HistoryItem *item); void unregItem(HistoryItem *item);
void updateFrom(const MTPMessageMedia &media); void updateFrom(const MTPMessageMedia &media);
bool updateStickerEmoji();
private: private:
int16 pixw, pixh;
DocumentData *data; DocumentData *data;
QString _emoji;
}; };
@ -1577,6 +1587,7 @@ public:
void updateMedia(const MTPMessageMedia &media) { void updateMedia(const MTPMessageMedia &media) {
if (_media) _media->updateFrom(media); if (_media) _media->updateFrom(media);
} }
void updateStickerEmoji();
QString selectedText(uint32 selection) const; QString selectedText(uint32 selection) const;
HistoryMedia *getMedia(bool inOverview = false) const; HistoryMedia *getMedia(bool inOverview = false) const;

View File

@ -1774,9 +1774,11 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
const MTPDmessages_allStickers &d(stickers.c_messages_allStickers()); const MTPDmessages_allStickers &d(stickers.c_messages_allStickers());
AllStickers all; AllStickers all;
EmojiStickersMap map;
const QVector<MTPDocument> &docs(d.vdocuments.c_vector().v); const QVector<MTPDocument> &docs(d.vdocuments.c_vector().v);
QSet<DocumentData*> found;
const RecentStickerPack &recent(cRecentStickers()); const RecentStickerPack &recent(cRecentStickers());
RecentStickerPack add; RecentStickerPack add;
add.reserve(docs.size()); add.reserve(docs.size());
@ -1786,13 +1788,32 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
if (!doc) continue; if (!doc) continue;
int32 j = 0, s = recent.size(); int32 j = 0, s = recent.size();
for (; j < s; ++j) { 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; if (j < s) continue;
add.push_back(qMakePair(doc, addValue)); add.push_back(qMakePair(doc, addValue));
} }
if (!add.isEmpty()) { bool needRemove = false;
cSetRecentStickers(add + recent); 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(); Local::writeRecentStickers();
_emojiPan.onTabChange(); _emojiPan.onTabChange();
} }
@ -1828,7 +1849,9 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
StickerPack &pack(all[e]); StickerPack &pack(all[e]);
pack.reserve(pack.size() + docs.size()); pack.reserve(pack.size() + docs.size());
for (int32 j = 0, s = docs.size(); j < s; ++j) { 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 { } else {
@ -1839,6 +1862,17 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
cSetStickers(all); cSetStickers(all);
cSetStickersHash(qba(d.vhash)); 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(); // updateStickerPan();
_emojiPan.onTabChange(); _emojiPan.onTabChange();
@ -2995,11 +3029,11 @@ void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType ty
imageLoader.append(files, histPeer->id, type); imageLoader.append(files, histPeer->id, type);
} }
void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type) { void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer) {
if (!hist) return; if (!peer && !hist) return;
App::wnd()->activateWindow(); App::wnd()->activateWindow();
imageLoader.append(fileContent, histPeer->id, type); imageLoader.append(fileContent, peer ? peer : histPeer->id, type);
} }
void HistoryWidget::onPhotoReady() { void HistoryWidget::onPhotoReady() {
@ -3089,7 +3123,7 @@ void HistoryWidget::onPhotoUploaded(MsgId newId, const MTPInputFile &file) {
uint64 randomId = MTP::nonce<uint64>(); uint64 randomId = MTP::nonce<uint64>();
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
History *hist = item->history(); 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);
} }
} }

View File

@ -298,7 +298,7 @@ public:
void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText = false); void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText = false);
void uploadConfirmImageUncompressed(bool ctrlShiftEnter); void uploadConfirmImageUncompressed(bool ctrlShiftEnter);
void uploadMedias(const QStringList &files, ToPrepareMediaType type); 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 confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname);
void confirmSendImage(const ReadyLocalMedia &img); void confirmSendImage(const ReadyLocalMedia &img);
void cancelSendImage(); void cancelSendImage();

View File

@ -40,6 +40,10 @@ public:
return res; return res;
} }
void mousePressEvent(QMouseEvent *e) {
e->accept();
}
signals: signals:
void closed(); void closed();

View File

@ -31,12 +31,13 @@ LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLo
}; };
void LocalImageLoaderPrivate::prepareImages() { void LocalImageLoaderPrivate::prepareImages() {
QString file, filename, mime; QString file, filename, mime, stickerMime = qsl("image/webp");
int32 filesize = 0; int32 filesize = 0;
QImage img; QImage img;
QByteArray data; QByteArray data;
PeerId peer; PeerId peer;
uint64 id, jpeg_id = 0; uint64 id, thumbId = 0;
QString thumbExt = "jpg";
ToPrepareMediaType type; ToPrepareMediaType type;
bool animated = false; bool animated = false;
bool ctrlShiftEnter = false; bool ctrlShiftEnter = false;
@ -72,12 +73,13 @@ void LocalImageLoaderPrivate::prepareImages() {
type = ToPrepareDocument; type = ToPrepareDocument;
} }
} }
if (type != ToPrepareAuto && info.size() < MaxUploadPhotoSize) {
img = App::readImage(file, 0, true, &animated);
}
if (type == ToPrepareDocument) { if (type == ToPrepareDocument) {
mime = mimeTypeForFile(info).name(); mime = mimeTypeForFile(info).name();
} }
if (type != ToPrepareAuto && info.size() < MaxUploadPhotoSize) {
bool opaque = (mime != stickerMime);
img = App::readImage(file, 0, opaque, &animated);
}
filename = info.fileName(); filename = info.fileName();
filesize = info.size(); filesize = info.size();
} else if (!data.isEmpty()) { } else if (!data.isEmpty()) {
@ -95,10 +97,15 @@ void LocalImageLoaderPrivate::prepareImages() {
if (type == ToPrepareDocument) { if (type == ToPrepareDocument) {
mime = mimeType.name(); mime = mimeType.name();
} }
filename = qsl("Document"); if (mime == "image/jpeg") {
QStringList patterns = mimeType.globPatterns(); filename = filedialogDefaultName(qsl("image"), qsl(".jpg"), QString(), true);
if (!patterns.isEmpty()) { } else {
filename = patterns.front().replace('*', filename); QString ext;
QStringList patterns = mimeType.globPatterns();
if (!patterns.isEmpty()) {
ext = patterns.front().replace('*', QString());
}
filename = filedialogDefaultName(qsl("doc"), ext, QString(), true);
} }
filesize = data.size(); filesize = data.size();
} }
@ -113,7 +120,15 @@ void LocalImageLoaderPrivate::prepareImages() {
} }
filesize = data.size(); filesize = data.size();
} else { } 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"); filename = qsl("Untitled.jpg");
filesize = 0; 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<MTPPhotoSize>(photoSizes)); photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
jpeg_id = id; thumbId = id;
} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) { } else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) {
int32 w = img.width(), h = img.height(); int32 w = img.width(), h = img.height();
QByteArray thumbFormat = "JPG"; QByteArray thumbFormat = "JPG";
int32 thumbQuality = 87;
if (animated) { if (animated) {
attributes.push_back(MTP_documentAttributeAnimated()); 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()); attributes.push_back(MTP_documentAttributeSticker());
thumbFormat = "webp"; thumbFormat = "webp";
thumbExt = qsl("webp");
} }
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h))); 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);
}
{ photoThumbs.insert('0', full);
QBuffer jpegBuffer(&jpeg); 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));
full.save(&jpegBuffer, thumbFormat, 87);
thumbId = MTP::nonce<uint64>();
} }
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<uint64>();
} }
if (type == ToPrepareDocument) { if (type == ToPrepareDocument) {
@ -194,7 +212,7 @@ void LocalImageLoaderPrivate::prepareImages() {
{ {
QMutexLocker lock(loader->readyMutex()); 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));
} }
{ {

View File

@ -43,8 +43,8 @@ typedef QList<ToPrepareMedia> ToPrepareMedias;
typedef QMap<int32, QByteArray> LocalFileParts; typedef QMap<int32, QByteArray> LocalFileParts;
struct ReadyLocalMedia { 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) : 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), jpeg_id(jpeg_id), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(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()) { if (!jpeg.isEmpty()) {
int32 size = jpeg.size(); int32 size = jpeg.size();
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) { for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
@ -58,7 +58,8 @@ struct ReadyLocalMedia {
QString file, filename; QString file, filename;
int32 filesize; int32 filesize;
QByteArray data; 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; PeerId peer;
MTPPhoto photo; MTPPhoto photo;

View File

@ -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<uint64> 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<HistoryPhoto*>(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<uint64> 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() { void MainWidget::forwardSelectedItems() {
if (overview) { if (overview) {
overview->onForwardSelected(); overview->onForwardSelected();

View File

@ -277,6 +277,8 @@ public:
void checkPeerHistory(PeerData *peer); void checkPeerHistory(PeerData *peer);
void checkedHistory(PeerData *peer, const MTPmessages_Messages &result); void checkedHistory(PeerData *peer, const MTPmessages_Messages &result);
bool sendPhotoFailed(uint64 randomId, const RPCError &e);
void forwardSelectedItems(); void forwardSelectedItems();
void deleteSelectedItems(); void deleteSelectedItems();
void clearSelectedItems(); void clearSelectedItems();
@ -359,6 +361,9 @@ public slots:
void onForwardCancel(QObject *obj = 0); void onForwardCancel(QObject *obj = 0);
void onResendAsDocument();
void onCancelResend();
private: private:
void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result); void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result);
@ -370,6 +375,8 @@ private:
QString failedFileName; QString failedFileName;
void loadFailed(mtpFileLoader *loader, bool started, const char *retrySlot); void loadFailed(mtpFileLoader *loader, bool started, const char *retrySlot);
QList<uint64> _resendImgRandomIds;
void gotDifference(const MTPupdates_Difference &diff); void gotDifference(const MTPupdates_Difference &diff);
bool failDifference(const RPCError &e); bool failDifference(const RPCError &e);
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other); void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);

View File

@ -74,6 +74,9 @@ RecentEmojiPreload gRecentEmojisPreload;
AllStickers gStickers; AllStickers gStickers;
QByteArray gStickersHash; QByteArray gStickersHash;
EmojiStickersMap gEmojiStickers;
RecentStickerPack gRecentStickers; RecentStickerPack gRecentStickers;
int32 gLang = -2; // auto int32 gLang = -2; // auto

View File

@ -150,6 +150,10 @@ typedef QVector<DocumentData*> StickerPack;
typedef QMap<EmojiPtr, StickerPack> AllStickers; typedef QMap<EmojiPtr, StickerPack> AllStickers;
DeclareSetting(AllStickers, Stickers); DeclareSetting(AllStickers, Stickers);
DeclareSetting(QByteArray, StickersHash); DeclareSetting(QByteArray, StickersHash);
typedef QMap<DocumentData*, EmojiPtr> EmojiStickersMap;
DeclareSetting(EmojiStickersMap, EmojiStickers);
typedef QList<QPair<DocumentData*, int16> > RecentStickerPack; typedef QList<QPair<DocumentData*, int16> > RecentStickerPack;
DeclareSetting(RecentStickerPack, RecentStickers); DeclareSetting(RecentStickerPack, RecentStickers);

View File

@ -944,7 +944,7 @@ void SettingsInner::onChangeLanguage() {
connect(box, SIGNAL(confirmed()), this, SLOT(onSaveTestLang())); connect(box, SIGNAL(confirmed()), this, SLOT(onSaveTestLang()));
App::wnd()->showLayer(box); App::wnd()->showLayer(box);
} else { } 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 { } else {