|
|
|
@ -3182,7 +3182,7 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo
|
|
|
|
|
, _openl(new PhotoLink(_data))
|
|
|
|
|
, _pixw(1)
|
|
|
|
|
, _pixh(1)
|
|
|
|
|
, _caption(st::minPhotoSize) {
|
|
|
|
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
|
|
|
|
if (!caption.isEmpty()) {
|
|
|
|
|
_caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
|
|
|
|
|
}
|
|
|
|
@ -3193,8 +3193,7 @@ HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width)
|
|
|
|
|
, _data(App::feedPhoto(photo))
|
|
|
|
|
, _openl(new PhotoLink(_data, chat))
|
|
|
|
|
, _pixw(1)
|
|
|
|
|
, _pixh(1)
|
|
|
|
|
, _caption(st::minPhotoSize) {
|
|
|
|
|
, _pixh(1) {
|
|
|
|
|
w = width;
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
@ -3225,18 +3224,21 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) {
|
|
|
|
|
|
|
|
|
|
if (parent->toHistoryMessage()) {
|
|
|
|
|
w = tw;
|
|
|
|
|
int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
|
int32 maxActualWidth = qMax(w, minWidth);
|
|
|
|
|
_maxw = qMax(maxActualWidth, th);
|
|
|
|
|
_minh = qMax(th, int32(st::minPhotoSize));
|
|
|
|
|
if (bubble) {
|
|
|
|
|
maxActualWidth += st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
_minh += st::mediaCaptionSkip + _caption.countHeight(maxActualWidth - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
th = w; // square chat photo updates
|
|
|
|
|
}
|
|
|
|
|
_maxw = qMax(qMax(w, int32(st::minPhotoSize)), th);
|
|
|
|
|
_minh = qMax(th, int32(st::minPhotoSize));
|
|
|
|
|
if (bubble) {
|
|
|
|
|
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
_maxw = qMax(_maxw, st::msgPadding.left() + _caption.maxWidth() + st::msgPadding.right());
|
|
|
|
|
_minh += st::mediaCaptionSkip + _caption.minHeight() + st::msgPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
_maxw = _minh = w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3269,7 +3271,9 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) {
|
|
|
|
|
}
|
|
|
|
|
if (_pixw < 1) _pixw = 1;
|
|
|
|
|
if (_pixh < 1) _pixh = 1;
|
|
|
|
|
w = qMax(_pixw, int16(st::minPhotoSize));
|
|
|
|
|
|
|
|
|
|
int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
|
w = qMax(_pixw, int16(minWidth));
|
|
|
|
|
_height = qMax(_pixh, int16(st::minPhotoSize));
|
|
|
|
|
if (bubble) {
|
|
|
|
|
_height += st::mediaPadding.top() + st::mediaPadding.bottom();
|
|
|
|
@ -3399,7 +3403,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b
|
|
|
|
|
pix = _data->thumb->pixBlurredSingle(_pixw, _pixh, width, height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.drawPixmap(skipx, skipy, pix);
|
|
|
|
|
p.drawPixmapLeft(skipx, skipy, w, pix);
|
|
|
|
|
if (!full) {
|
|
|
|
|
uint64 dt = itemAnimations().animate(parent, ms);
|
|
|
|
|
int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
|
|
|
|
@ -3422,7 +3426,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selected) {
|
|
|
|
|
App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
|
|
|
|
|
App::roundRect(p, rtlrect(skipx, skipy, width, height, w), textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// date
|
|
|
|
@ -3481,232 +3485,53 @@ QString formatDurationAndSizeText(qint64 duration, qint64 size) {
|
|
|
|
|
return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString formatGifAndSizeText(qint64 size) {
|
|
|
|
|
return lng_duration_and_size(lt_duration, qsl("GIF"), lt_size, formatSizeText(size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString formatPlayedText(qint64 played, qint64 duration) {
|
|
|
|
|
return lng_duration_played(lt_played, formatDurationText(played), lt_duration, formatDurationText(duration));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryMedia()
|
|
|
|
|
, data(App::feedVideo(video))
|
|
|
|
|
, _openl(new VideoOpenLink(data))
|
|
|
|
|
, _savel(new VideoSaveLink(data))
|
|
|
|
|
, _cancell(new VideoCancelLink(data))
|
|
|
|
|
, _caption(st::minPhotoSize)
|
|
|
|
|
, _dldDone(0)
|
|
|
|
|
, _uplDone(0) {
|
|
|
|
|
if (!caption.isEmpty()) {
|
|
|
|
|
_caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
|
|
|
|
|
namespace {
|
|
|
|
|
QString documentName(DocumentData *document) {
|
|
|
|
|
SongData *song = document->song();
|
|
|
|
|
if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name;
|
|
|
|
|
|
|
|
|
|
if (song->performer.isEmpty()) return song->title;
|
|
|
|
|
|
|
|
|
|
return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_size = formatDurationAndSizeText(data->duration, data->size);
|
|
|
|
|
|
|
|
|
|
data->thumb->load();
|
|
|
|
|
|
|
|
|
|
int32 tw = data->thumb->width(), th = data->thumb->height();
|
|
|
|
|
if (data->thumb->isNull() || !tw || !th) {
|
|
|
|
|
_thumbw = 0;
|
|
|
|
|
} else if (tw > th) {
|
|
|
|
|
_thumbw = (tw * st::mediaThumbSize) / th;
|
|
|
|
|
} else {
|
|
|
|
|
_thumbw = st::mediaThumbSize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::initDimensions(const HistoryItem *parent) {
|
|
|
|
|
if (_caption.hasSkipBlock()) _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
|
|
|
|
|
|
|
|
|
|
_maxw = st::msgMaxWidth;
|
|
|
|
|
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
|
|
|
|
|
|
|
|
|
|
_minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
|
|
|
|
|
if (_caption.isEmpty()) {
|
|
|
|
|
_height = _minh;
|
|
|
|
|
} else {
|
|
|
|
|
_minh += st::webPagePhotoSkip + _caption.minHeight();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::regItem(HistoryItem *item) {
|
|
|
|
|
App::regVideoItem(data, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::unregItem(HistoryItem *item) {
|
|
|
|
|
App::unregVideoItem(data, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString HistoryVideo::inDialogsText() const {
|
|
|
|
|
return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString HistoryVideo::inHistoryText() const {
|
|
|
|
|
return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
|
|
|
|
|
int32 height = _height;
|
|
|
|
|
if (width < 0) {
|
|
|
|
|
width = w;
|
|
|
|
|
} else if (!_caption.isEmpty()) {
|
|
|
|
|
height = countHeight(parent, width);
|
|
|
|
|
}
|
|
|
|
|
if (width >= _maxw) {
|
|
|
|
|
width = _maxw;
|
|
|
|
|
}
|
|
|
|
|
return (x >= 0 && y >= 0 && x < width && y < height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const {
|
|
|
|
|
if (_caption.isEmpty()) return _height;
|
|
|
|
|
|
|
|
|
|
if (width < 0) width = w;
|
|
|
|
|
if (width >= _maxw) {
|
|
|
|
|
width = _maxw;
|
|
|
|
|
int32 videoMaxStatusWidth(VideoData *video) {
|
|
|
|
|
int32 result = st::normalFont->width(formatDownloadText(video->size, video->size));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatDurationAndSizeText(video->duration, video->size)));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
int32 textw = width - st::mediaPadding.left() - st::mediaPadding.right();
|
|
|
|
|
h += st::webPagePhotoSkip + _caption.countHeight(textw);
|
|
|
|
|
}
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
|
|
|
|
|
int32 height = _height;
|
|
|
|
|
if (width < 0) {
|
|
|
|
|
width = w;
|
|
|
|
|
} else if (!_caption.isEmpty()) {
|
|
|
|
|
height = countHeight(parent, width);
|
|
|
|
|
}
|
|
|
|
|
if (width < 1) return;
|
|
|
|
|
|
|
|
|
|
int skipy = 0, replyFrom = 0, fwdFrom = 0;
|
|
|
|
|
|
|
|
|
|
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
|
|
|
|
|
if (width >= _maxw) {
|
|
|
|
|
width = _maxw;
|
|
|
|
|
int32 audioMaxStatusWidth(AudioData *audio) {
|
|
|
|
|
int32 result = st::normalFont->width(formatDownloadText(audio->size, audio->size));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatPlayedText(audio->duration, audio->duration)));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatDurationAndSizeText(audio->duration, audio->size)));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool inDate = parent->pointInTime(width, height, x, y, InfoDisplayDefault);
|
|
|
|
|
if (inDate) {
|
|
|
|
|
state = HistoryInDateCursorState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 tw = width - st::mediaPadding.left() - st::mediaPadding.right();
|
|
|
|
|
if (x >= st::mediaPadding.left() && y >= skipy + st::mediaPadding.top() && x < st::mediaPadding.left() + tw && y < skipy + st::mediaPadding.top() + st::mediaThumbSize && !data->loader && data->access) {
|
|
|
|
|
lnk = _openl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!_caption.isEmpty() && x >= st::mediaPadding.left() && x < st::mediaPadding.left() + tw && y >= skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip) {
|
|
|
|
|
bool inText = false;
|
|
|
|
|
_caption.getState(lnk, inText, x - st::mediaPadding.left(), y - skipy - st::mediaPadding.top() - st::mediaThumbSize - st::webPagePhotoSkip, tw);
|
|
|
|
|
state = inDate ? HistoryInDateCursorState : (inText ? HistoryInTextCursorState : HistoryDefaultCursorState);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HistoryMedia *HistoryVideo::clone() const {
|
|
|
|
|
return new HistoryVideo(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
|
|
|
|
|
if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
|
|
|
|
int32 width = w, height = _height, skipx = 0, skipy = 0;
|
|
|
|
|
|
|
|
|
|
data->thumb->checkload();
|
|
|
|
|
|
|
|
|
|
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
|
|
|
|
|
if (width >= _maxw) {
|
|
|
|
|
width = _maxw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg));
|
|
|
|
|
style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow));
|
|
|
|
|
RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
|
|
|
|
|
App::roundRect(p, 0, 0, width, height, bg, cors, &sh);
|
|
|
|
|
|
|
|
|
|
//if (_thumbw) {
|
|
|
|
|
// p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
|
|
|
|
|
//} else {
|
|
|
|
|
// p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (outbg ? st::mediaDocOutImg : st::mediaDocInImg));
|
|
|
|
|
//}
|
|
|
|
|
if (selected) {
|
|
|
|
|
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
|
|
|
|
|
int32 twidth = width - tleft - st::mediaPadding.right();
|
|
|
|
|
int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
|
|
|
|
|
|
|
|
|
|
p.setFont(st::normalFont->f);
|
|
|
|
|
p.setPen(st::black->c);
|
|
|
|
|
p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, lang(lng_media_video));
|
|
|
|
|
|
|
|
|
|
QString statusText;
|
|
|
|
|
|
|
|
|
|
style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg));
|
|
|
|
|
p.setPen(status->p);
|
|
|
|
|
|
|
|
|
|
if (data->loader) {
|
|
|
|
|
int32 offset = data->loader->currentOffset();
|
|
|
|
|
if (_dldTextCache.isEmpty() || _dldDone != offset) {
|
|
|
|
|
_dldDone = offset;
|
|
|
|
|
_dldTextCache = formatDownloadText(_dldDone, data->size);
|
|
|
|
|
}
|
|
|
|
|
statusText = _dldTextCache;
|
|
|
|
|
} else {
|
|
|
|
|
if (data->status == FileUploadFailed || data->status == FileDownloadFailed) {
|
|
|
|
|
statusText = lang(lng_attach_failed);
|
|
|
|
|
} else if (data->status == FileUploading) {
|
|
|
|
|
if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
|
|
|
|
|
_uplDone = data->uploadOffset;
|
|
|
|
|
_uplTextCache = formatDownloadText(_uplDone, data->size);
|
|
|
|
|
}
|
|
|
|
|
statusText = _uplTextCache;
|
|
|
|
|
int32 documentMaxStatusWidth(DocumentData *document) {
|
|
|
|
|
int32 result = st::normalFont->width(formatDownloadText(document->size, document->size));
|
|
|
|
|
if (SongData *song = document->song()) {
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatPlayedText(song->duration, song->duration)));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatDurationAndSizeText(song->duration, document->size)));
|
|
|
|
|
} else {
|
|
|
|
|
statusText = _size;
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatSizeText(document->size)));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height;
|
|
|
|
|
p.drawText(tleft, texty + st::normalFont->ascent, statusText);
|
|
|
|
|
if (parent->isMediaUnread()) {
|
|
|
|
|
int32 w = st::normalFont->width(statusText);
|
|
|
|
|
if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= twidth) {
|
|
|
|
|
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
|
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
|
p.setBrush((outbg ? (selected ? st::mediaOutUnreadFgSelected : st::mediaOutUnreadFg) : (selected ? st::mediaInUnreadFgSelected : st::mediaInUnreadFg))->b);
|
|
|
|
|
p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize));
|
|
|
|
|
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
p.setPen(st::black->p);
|
|
|
|
|
_caption.draw(p, st::mediaPadding.left(), skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip, width - st::mediaPadding.left() - st::mediaPadding.right());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) {
|
|
|
|
|
w = qMin(width, _maxw);
|
|
|
|
|
if (_caption.isEmpty()) return _height;
|
|
|
|
|
|
|
|
|
|
_height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
int32 textw = w - st::mediaPadding.left() - st::mediaPadding.right();
|
|
|
|
|
_height += st::webPagePhotoSkip + _caption.countHeight(textw);
|
|
|
|
|
int32 gifMaxStatusWidth(DocumentData *document) {
|
|
|
|
|
int32 result = st::normalFont->width(formatDownloadText(document->size, document->size));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatGifAndSizeText(document->size)));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return _height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImagePtr HistoryVideo::replyPreview() {
|
|
|
|
|
if (data->replyPreview->isNull() && !data->thumb->isNull()) {
|
|
|
|
|
if (data->thumb->loaded()) {
|
|
|
|
|
int w = data->thumb->width(), h = data->thumb->height();
|
|
|
|
|
if (w <= 0) w = 1;
|
|
|
|
|
if (h <= 0) h = 1;
|
|
|
|
|
data->replyPreview = ImagePtr(w > h ? data->thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : data->thumb->pix(st::msgReplyBarSize.height()), "PNG");
|
|
|
|
|
} else {
|
|
|
|
|
data->thumb->load();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return data->replyPreview;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HistoryFileMedia::HistoryFileMedia() : HistoryMedia()
|
|
|
|
@ -3797,33 +3622,322 @@ HistoryFileMedia::~HistoryFileMedia() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
QString documentName(DocumentData *document) {
|
|
|
|
|
SongData *song = document->song();
|
|
|
|
|
if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name;
|
|
|
|
|
|
|
|
|
|
if (song->performer.isEmpty()) return song->title;
|
|
|
|
|
|
|
|
|
|
return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title);
|
|
|
|
|
HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryFileMedia()
|
|
|
|
|
, _data(App::feedVideo(video))
|
|
|
|
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
|
|
|
|
, _thumbw(1) {
|
|
|
|
|
if (!caption.isEmpty()) {
|
|
|
|
|
_caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 documentMaxStatusWidth(DocumentData *document) {
|
|
|
|
|
int32 result = st::normalFont->width(formatDownloadText(document->size, document->size));
|
|
|
|
|
if (SongData *song = document->song()) {
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatPlayedText(song->duration, song->duration)));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatDurationAndSizeText(song->duration, document->size)));
|
|
|
|
|
} else {
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatSizeText(document->size)));
|
|
|
|
|
setLinks(new VideoOpenLink(_data), new VideoSaveLink(_data), new VideoCancelLink(_data));
|
|
|
|
|
|
|
|
|
|
setStatusSize(FileStatusSizeReady);
|
|
|
|
|
|
|
|
|
|
_data->thumb->load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::initDimensions(const HistoryItem *parent) {
|
|
|
|
|
bool bubble = parent->hasBubble();
|
|
|
|
|
|
|
|
|
|
if (_caption.hasSkipBlock()) {
|
|
|
|
|
_caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height());
|
|
|
|
|
if (!tw || !th) {
|
|
|
|
|
tw = th = 1;
|
|
|
|
|
}
|
|
|
|
|
if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
|
|
|
|
|
th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
|
|
|
|
|
tw = st::msgVideoSize.width();
|
|
|
|
|
} else {
|
|
|
|
|
tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
|
|
|
|
|
th = st::msgVideoSize.height();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w = _thumbw = qMax(tw, 1);
|
|
|
|
|
int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
|
minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
|
_maxw = qMax(w, minWidth);
|
|
|
|
|
_minh = qMax(th, int32(st::minPhotoSize));
|
|
|
|
|
if (bubble) {
|
|
|
|
|
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
_minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::setStatusSize(int32 newSize) const {
|
|
|
|
|
HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::updateStatusText(const HistoryItem *parent) const {
|
|
|
|
|
bool showPause = false;
|
|
|
|
|
int32 statusSize = 0, realDuration = 0;
|
|
|
|
|
if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
|
|
|
|
|
statusSize = FileStatusSizeFailed;
|
|
|
|
|
} else if (_data->status == FileUploading) {
|
|
|
|
|
statusSize = _data->uploadOffset;
|
|
|
|
|
} else if (_data->loader) {
|
|
|
|
|
statusSize = _data->loader->currentOffset();
|
|
|
|
|
} else if (!_data->already().isEmpty()) {
|
|
|
|
|
statusSize = FileStatusSizeLoaded;
|
|
|
|
|
} else {
|
|
|
|
|
statusSize = FileStatusSizeReady;
|
|
|
|
|
}
|
|
|
|
|
if (statusSize != _statusSize) {
|
|
|
|
|
setStatusSize(statusSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::regItem(HistoryItem *item) {
|
|
|
|
|
App::regVideoItem(_data, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::unregItem(HistoryItem *item) {
|
|
|
|
|
App::unregVideoItem(_data, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString HistoryVideo::inDialogsText() const {
|
|
|
|
|
return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString HistoryVideo::inHistoryText() const {
|
|
|
|
|
return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
|
|
|
|
|
if (width < 0) width = w;
|
|
|
|
|
return (x >= 0 && y >= 0 && x < width && y < _height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const {
|
|
|
|
|
if (width < 0) width = w;
|
|
|
|
|
if (width >= _maxw) {
|
|
|
|
|
width = _maxw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 audioMaxStatusWidth(AudioData *audio) {
|
|
|
|
|
int32 result = st::normalFont->width(formatDownloadText(audio->size, audio->size));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatPlayedText(audio->duration, audio->duration)));
|
|
|
|
|
result = qMax(result, st::normalFont->width(formatDurationAndSizeText(audio->duration, audio->size)));
|
|
|
|
|
return result;
|
|
|
|
|
bool bubble = parent->hasBubble();
|
|
|
|
|
|
|
|
|
|
int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height());
|
|
|
|
|
if (!tw || !th) {
|
|
|
|
|
tw = th = 1;
|
|
|
|
|
}
|
|
|
|
|
if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
|
|
|
|
|
th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
|
|
|
|
|
tw = st::msgVideoSize.width();
|
|
|
|
|
} else {
|
|
|
|
|
tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
|
|
|
|
|
th = st::msgVideoSize.height();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bubble) {
|
|
|
|
|
width -= st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
}
|
|
|
|
|
if (width < tw) {
|
|
|
|
|
th = qRound((width / float64(tw)) * th);
|
|
|
|
|
tw = width;
|
|
|
|
|
}
|
|
|
|
|
int32 h = qMax(th, int32(st::minPhotoSize));
|
|
|
|
|
if (bubble) {
|
|
|
|
|
tw += st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
h += st::mediaPadding.top() + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
h += st::mediaCaptionSkip + _caption.countHeight(tw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
|
|
|
|
|
bool bubble = parent->hasBubble();
|
|
|
|
|
|
|
|
|
|
if (width < 0) width = w;
|
|
|
|
|
if (width < 1) return;
|
|
|
|
|
|
|
|
|
|
int skipx = 0, skipy = 0, height = _height;
|
|
|
|
|
if (bubble) {
|
|
|
|
|
skipx = st::mediaPadding.left();
|
|
|
|
|
skipy = st::mediaPadding.top();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
|
|
|
|
|
height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
|
|
|
|
|
if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
|
|
|
|
|
bool inText = false;
|
|
|
|
|
_caption.getState(lnk, inText, x - st::msgPadding.left(), y - height, captionw);
|
|
|
|
|
state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
|
|
|
|
|
}
|
|
|
|
|
height -= st::mediaCaptionSkip;
|
|
|
|
|
}
|
|
|
|
|
width -= st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
height -= skipy + st::mediaPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) {
|
|
|
|
|
lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl;
|
|
|
|
|
if (_caption.isEmpty()) {
|
|
|
|
|
int32 fullRight = skipx + width, fullBottom = skipy + height;
|
|
|
|
|
bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage);
|
|
|
|
|
if (inDate) {
|
|
|
|
|
state = HistoryInDateCursorState;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HistoryMedia *HistoryVideo::clone() const {
|
|
|
|
|
return new HistoryVideo(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
|
|
|
|
|
if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
|
|
|
|
int32 width = w, height = _height, skipx = 0, skipy = 0;
|
|
|
|
|
int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
|
|
|
|
|
|
|
|
|
|
bool bubble = parent->hasBubble();
|
|
|
|
|
bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel;
|
|
|
|
|
|
|
|
|
|
if (_data->loader) {
|
|
|
|
|
ensureAnimation(parent);
|
|
|
|
|
if (!_animation->radial.animating()) {
|
|
|
|
|
_animation->radial.start(_data->progress());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
updateStatusText(parent);
|
|
|
|
|
bool radial = isRadialAnimation(ms);
|
|
|
|
|
|
|
|
|
|
if (bubble) {
|
|
|
|
|
skipx = st::mediaPadding.left();
|
|
|
|
|
skipy = st::mediaPadding.top();
|
|
|
|
|
|
|
|
|
|
width -= st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
height -= skipy + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
|
|
|
|
}
|
|
|
|
|
_data->thumb->checkload();
|
|
|
|
|
|
|
|
|
|
QRect rthumb(rtlrect(skipx, skipy, width, height, w));
|
|
|
|
|
|
|
|
|
|
QPixmap pix = _data->thumb->pixBlurredSingle(_thumbw, 0, width, height);
|
|
|
|
|
p.drawPixmap(rthumb.topLeft(), pix);
|
|
|
|
|
if (selected) {
|
|
|
|
|
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
|
if (selected) {
|
|
|
|
|
p.setBrush(st::msgDateImgBgSelected);
|
|
|
|
|
} else if (_animation && _animation->_a_thumbOver.animating()) {
|
|
|
|
|
_animation->_a_thumbOver.step(ms);
|
|
|
|
|
float64 over = _animation->a_thumbOver.current();
|
|
|
|
|
p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
|
|
|
|
|
p.setBrush(st::black);
|
|
|
|
|
} else {
|
|
|
|
|
bool over = textlnkDrawOver(_data->loader ? _cancell : _savel);
|
|
|
|
|
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.setRenderHint(QPainter::HighQualityAntialiasing);
|
|
|
|
|
p.drawEllipse(inner);
|
|
|
|
|
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
|
|
|
|
|
|
|
|
|
|
if (!selected && _animation) {
|
|
|
|
|
p.setOpacity(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
style::sprite icon;
|
|
|
|
|
if (!_data->already().isEmpty()) {
|
|
|
|
|
icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
|
|
|
|
|
} else if (_data->loader) {
|
|
|
|
|
icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
|
|
|
|
|
} else {
|
|
|
|
|
icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
|
|
|
|
|
}
|
|
|
|
|
p.drawSpriteCenter(inner, icon);
|
|
|
|
|
if (radial) {
|
|
|
|
|
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
|
|
|
|
_animation->radial.draw(p, rinner, selected ? st::msgInBgSelected : st::msgInBg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y();
|
|
|
|
|
int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
|
|
|
|
|
int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
|
|
|
|
|
App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
|
|
|
|
|
p.setFont(st::normalFont);
|
|
|
|
|
p.setPen(st::white);
|
|
|
|
|
p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x());
|
|
|
|
|
|
|
|
|
|
// date
|
|
|
|
|
QString time(parent->timeText());
|
|
|
|
|
if (_caption.isEmpty()) {
|
|
|
|
|
int32 fullRight = skipx + width, fullBottom = skipy + height;
|
|
|
|
|
parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
|
|
|
|
|
} else {
|
|
|
|
|
p.setPen(st::black);
|
|
|
|
|
_caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) {
|
|
|
|
|
bool bubble = parent->hasBubble();
|
|
|
|
|
|
|
|
|
|
int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height());
|
|
|
|
|
if (!tw || !th) {
|
|
|
|
|
tw = th = 1;
|
|
|
|
|
}
|
|
|
|
|
if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
|
|
|
|
|
th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
|
|
|
|
|
tw = st::msgVideoSize.width();
|
|
|
|
|
} else {
|
|
|
|
|
tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
|
|
|
|
|
th = st::msgVideoSize.height();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bubble) {
|
|
|
|
|
width -= st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
}
|
|
|
|
|
if (width < tw) {
|
|
|
|
|
th = qRound((width / float64(tw)) * th);
|
|
|
|
|
tw = width;
|
|
|
|
|
}
|
|
|
|
|
w = _thumbw = tw;
|
|
|
|
|
|
|
|
|
|
int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
|
minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
|
|
|
|
w = qMax(w, minWidth);
|
|
|
|
|
|
|
|
|
|
_height = qMax(th, int32(st::minPhotoSize));
|
|
|
|
|
if (bubble) {
|
|
|
|
|
w += st::mediaPadding.left() + st::mediaPadding.right();
|
|
|
|
|
_height += st::mediaPadding.top() + st::mediaPadding.bottom();
|
|
|
|
|
if (!_caption.isEmpty()) {
|
|
|
|
|
int32 captionw = w - st::msgPadding.left() - st::msgPadding.right();
|
|
|
|
|
_height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return _height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImagePtr HistoryVideo::replyPreview() {
|
|
|
|
|
if (_data->replyPreview->isNull() && !_data->thumb->isNull()) {
|
|
|
|
|
if (_data->thumb->loaded()) {
|
|
|
|
|
int w = _data->thumb->width(), h = _data->thumb->height();
|
|
|
|
|
if (w <= 0) w = 1;
|
|
|
|
|
if (h <= 0) h = 1;
|
|
|
|
|
_data->replyPreview = ImagePtr(w > h ? _data->thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : _data->thumb->pix(st::msgReplyBarSize.height()), "PNG");
|
|
|
|
|
} else {
|
|
|
|
|
_data->thumb->load();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return _data->replyPreview;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryFileMedia()
|
|
|
|
|