audio documents playback added, audio documents suppress on voice message, and both suppress on notify added

This commit is contained in:
John Preston 2015-07-01 00:07:05 +03:00
parent 387694f477
commit f7d55005c4
19 changed files with 1422 additions and 477 deletions

View File

@ -1146,7 +1146,7 @@ notifyTextTop: 7px;
notifySlowHideFunc: transition(easeInCirc);
notifyWaitShortHide: 0;
notifyWaitLongHide: 20000;
notifyFastAnim: 100;
notifyFastAnim: 150;
notifyFastAnimFunc: transition(linear);
notifyWidth: 316px;
notifyHeight: 80px;
@ -1968,3 +1968,6 @@ webPagePhotoSize: 100px;
webPagePhotoDelta: 8px;
botDescSkip: 8px;
suppressAll: 0.2;
suppressSong: 0.05;

View File

@ -302,7 +302,7 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
int32 wasCount = -1;
for (int32 i = 0, l = d_docs.size(); i != l; ++i) {
DocumentData *doc = App::feedDocument(d_docs.at(i));
if (!doc || !doc->sticker) continue;
if (!doc || !doc->sticker()) continue;
if (wasCount < 0) wasCount = it->stickers.size();
if (it->stickers.indexOf(doc) < 0) {

View File

@ -1295,7 +1295,7 @@ namespace App {
}
convert->id = document;
convert->status = FileReady;
sentSticker = !!convert->sticker;
sentSticker = !!convert->sticker();
}
convert->access = access;
if (!convert->date && date) {
@ -1309,20 +1309,20 @@ namespace App {
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) {
convert->thumb = thumb;
}
if (convert->sticker && !attributes.isEmpty() && (convert->sticker->alt.isEmpty() || convert->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
if (convert->sticker() && !attributes.isEmpty() && (convert->sticker()->alt.isEmpty() || convert->sticker()->set.type() == mtpc_inputStickerSetEmpty)) {
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
if (i->type() == mtpc_documentAttributeSticker) {
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
if (d.valt.c_string().v.length() > 0) {
convert->sticker->alt = qs(d.valt);
convert->sticker->set = d.vstickerset;
convert->sticker()->alt = qs(d.valt);
convert->sticker()->set = d.vstickerset;
}
}
}
}
}
if (convert->sticker && !convert->sticker->loc.dc && thumbLocation.dc) {
convert->sticker->loc = thumbLocation;
if (convert->sticker() && !convert->sticker()->loc.dc && thumbLocation.dc) {
convert->sticker()->loc = thumbLocation;
}
if (convert->location.check()) {
@ -1336,7 +1336,7 @@ namespace App {
result = convert;
} else {
result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
if (result->sticker) result->sticker->loc = thumbLocation;
if (result->sticker()) result->sticker()->loc = thumbLocation;
}
documentsData.insert(document, result);
} else {
@ -1354,19 +1354,19 @@ namespace App {
if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
result->thumb = thumb;
}
if (result->sticker && !attributes.isEmpty() && (result->sticker->alt.isEmpty() || result->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
if (result->sticker() && !attributes.isEmpty() && (result->sticker()->alt.isEmpty() || result->sticker()->set.type() == mtpc_inputStickerSetEmpty)) {
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
if (i->type() == mtpc_documentAttributeSticker) {
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
if (d.valt.c_string().v.length() > 0) {
result->sticker->alt = qs(d.valt);
result->sticker->set = d.vstickerset;
result->sticker()->alt = qs(d.valt);
result->sticker()->set = d.vstickerset;
}
}
}
}
if (result->sticker && !result->sticker->loc.dc && thumbLocation.dc) {
result->sticker->loc = thumbLocation;
if (result->sticker() && !result->sticker()->loc.dc && thumbLocation.dc) {
result->sticker()->loc = thumbLocation;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -45,39 +45,59 @@ public:
AudioPlayer();
void play(AudioData *audio);
void pauseresume();
void play(const AudioMsgId &audio);
void play(const SongMsgId &song);
void pauseresume(MediaOverviewType type);
void currentState(AudioMsgId *audio, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
void currentState(SongMsgId *song, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
void clearStoppedAtStart(const AudioMsgId &audio);
void clearStoppedAtStart(const SongMsgId &song);
void currentState(AudioData **audio, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
void clearStoppedAtStart(AudioData *audio);
void resumeDevice();
~AudioPlayer();
public slots:
void onError(AudioData *audio);
void onError(const AudioMsgId &audio);
void onError(const SongMsgId &song);
void onStopped(const AudioMsgId &audio);
void onStopped(const SongMsgId &song);
signals:
void updated(AudioData *audio);
void stopped(AudioData *audio);
void updated(const AudioMsgId &audio);
void updated(const SongMsgId &song);
void stopped(const AudioMsgId &audio);
void stopped(const SongMsgId &song);
void loaderOnStart(const AudioMsgId &audio);
void loaderOnStart(const SongMsgId &song);
void loaderOnCancel(const AudioMsgId &audio);
void loaderOnCancel(const SongMsgId &song);
void faderOnTimer();
void loaderOnStart(AudioData *audio);
void loaderOnCancel(AudioData *audio);
void suppressSong();
void unsuppressSong();
void suppressAll();
private:
bool updateCurrentStarted(int32 pos = -1);
bool startedOther(MediaOverviewType type, bool &fadedStart);
bool updateCurrentStarted(MediaOverviewType type, int32 pos = -1);
struct Msg {
Msg() : audio(0), position(0), duration(0), frequency(AudioVoiceMsgFrequency), skipStart(0), skipEnd(0), loading(0), started(0),
Msg() : position(0), duration(0), frequency(AudioVoiceMsgFrequency), skipStart(0), skipEnd(0), loading(0), started(0),
state(AudioPlayerStopped), source(0), nextBuffer(0) {
memset(buffers, 0, sizeof(buffers));
memset(samplesCount, 0, sizeof(samplesCount));
}
AudioData *audio;
QString fname;
QByteArray data;
int64 position, duration;
@ -92,9 +112,24 @@ private:
uint32 buffers[3];
int64 samplesCount[3];
};
struct AudioMsg : public Msg {
AudioMsg() {
}
AudioMsgId audio;
};
struct SongMsg : public Msg {
SongMsg() {
}
SongMsgId song;
};
int32 _current;
Msg _data[AudioVoiceMsgSimultaneously];
void currentState(Msg *current, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency);
int32 _audioCurrent;
AudioMsg _audioData[AudioVoiceMsgSimultaneously];
int32 _songCurrent;
SongMsg _songData[AudioSongSimultaneously];
QMutex _mutex;
@ -154,10 +189,14 @@ public:
signals:
void error(AudioData *audio);
void playPositionUpdated(AudioData *audio);
void audioStopped(AudioData *audio);
void needToPreload(AudioData *audio);
void error(const AudioMsgId &audio);
void error(const SongMsgId &audio);
void playPositionUpdated(const AudioMsgId &audio);
void playPositionUpdated(const SongMsgId &audio);
void audioStopped(const AudioMsgId &audio);
void audioStopped(const SongMsgId &audio);
void needToPreload(const AudioMsgId &audio);
void needToPreload(const SongMsgId &audio);
void stopPauseDevice();
@ -168,12 +207,28 @@ public slots:
void onPauseTimer();
void onPauseTimerStop();
void onSuppressSong();
void onUnsuppressSong();
void onSuppressAll();
private:
enum {
EmitError = 0x01,
EmitStopped = 0x02,
EmitPositionUpdated = 0x04,
EmitNeedToPreload = 0x08,
};
int32 updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying, bool &hasFading, float64 suppressGain, bool suppressGainChanged);
QTimer _timer, _pauseTimer;
QMutex _pauseMutex;
bool _pauseFlag, _paused;
bool _suppressAll, _suppressAllAnim, _suppressSong, _suppressSongAnim;
anim::fvalue _suppressAllGain, _suppressSongGain;
uint64 _suppressAllStart, _suppressSongStart;
};
class AudioPlayerLoader;
@ -187,22 +242,45 @@ public:
signals:
void error(AudioData *audio);
void error(const AudioMsgId &audio);
void error(const SongMsgId &song);
void needToCheck();
public slots:
void onInit();
void onStart(AudioData *audio);
void onLoad(AudioData *audio);
void onCancel(AudioData *audio);
void onStart(const AudioMsgId &audio);
void onStart(const SongMsgId &audio);
void onLoad(const AudioMsgId &audio);
void onLoad(const SongMsgId &audio);
void onCancel(const AudioMsgId &audio);
void onCancel(const SongMsgId &audio);
private:
typedef QMap<AudioData*, AudioPlayerLoader*> Loaders;
Loaders _loaders;
AudioMsgId _audio;
AudioPlayerLoader *_audioLoader;
void loadError(Loaders::iterator i);
SongMsgId _song;
AudioPlayerLoader *_songLoader;
void emitError(MediaOverviewType type);
void clear(MediaOverviewType type);
AudioMsgId clearAudio();
SongMsgId clearSong();
enum SetupError {
SetupErrorAtStart = 0,
SetupErrorNotPlaying = 1,
SetupErrorLoadedFull = 2,
SetupNoErrorStarted = 3,
};
void loadData(MediaOverviewType type, const void *objId);
AudioPlayerLoader *setupLoader(MediaOverviewType type, const void *objId, SetupError &err);
AudioPlayer::Msg *checkLoader(MediaOverviewType type);
};
@ -239,3 +317,5 @@ private:
QByteArray _captured;
};
MTPDocumentAttribute audioReadSongAttributes(const QString &fname, const QByteArray &data, QImage &cover, QByteArray &coverBytes, QByteArray &coverFormat);

View File

@ -46,7 +46,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.reserve(v.size());
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker) continue;
if (!doc || !doc->sticker()) continue;
_pack.push_back(doc);
}
@ -156,11 +156,11 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
if (!doc->loader && doc->status != FileFailed && !already && !hasdata) {
doc->save(QString());
}
if (doc->sticker->img->isNull() && (already || hasdata)) {
if (doc->sticker()->img->isNull() && (already || hasdata)) {
if (already) {
doc->sticker->img = ImagePtr(doc->already());
doc->sticker()->img = ImagePtr(doc->already());
} else {
doc->sticker->img = ImagePtr(doc->data);
doc->sticker()->img = ImagePtr(doc->data);
}
}
}
@ -173,8 +173,8 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (goodThumb) {
p.drawPixmapLeft(ppos, width(), doc->thumb->pix(w, h));
} else if (!doc->sticker->img->isNull()) {
p.drawPixmapLeft(ppos, width(), doc->sticker->img->pix(w, h));
} else if (!doc->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), doc->sticker()->img->pix(w, h));
}
}
}

View File

@ -85,9 +85,10 @@ enum {
MediaOverviewPreloadCount = 4,
AudioVoiceMsgSimultaneously = 4,
AudioSongSimultaneously = 4,
AudioCheckPositionTimeout = 100, // 100ms per check audio pos
AudioCheckPositionDelta = 4800, // update position called each 4800 samples
AudioFadeTimeout = 10, // 10ms
AudioFadeTimeout = 7, // 7ms
AudioFadeDuration = 500,
AudioVoiceMsgSkip = 400, // 200ms
AudioVoiceMsgFade = 300, // 300ms
@ -100,7 +101,7 @@ enum {
AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded
AudioPauseDeviceTimeout = 3000, // pause in 3 secs after playing is over
StickerInMemory = 256 * 1024, // 128 Kb stickers hold in memory, auto loaded and displayed inline
StickerInMemory = 1024 * 1024, // 1024 Kb stickers hold in memory, auto loaded and displayed inline
StickerMaxSize = 2048, // 2048x2048 is a max image size for sticker
MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app

View File

@ -1216,7 +1216,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
float64 hover = _sets[c].hovers[index];
DocumentData *sticker = _sets[c].pack[index];
if (!sticker->sticker) continue;
if (!sticker->sticker()) continue;
QPoint pos(st::stickerPanPadding + j * st::stickerPanSize.width(), y + i * st::stickerPanSize.height());
if (hover > 0) {
@ -1235,11 +1235,11 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) {
sticker->save(QString());
}
if (sticker->sticker->img->isNull() && (already || hasdata)) {
if (sticker->sticker()->img->isNull() && (already || hasdata)) {
if (already) {
sticker->sticker->img = ImagePtr(sticker->already());
sticker->sticker()->img = ImagePtr(sticker->already());
} else {
sticker->sticker->img = ImagePtr(sticker->data);
sticker->sticker()->img = ImagePtr(sticker->data);
}
}
}
@ -1252,8 +1252,8 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
if (goodThumb) {
p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h));
} else if (!sticker->sticker->img->isNull()) {
p.drawPixmapLeft(ppos, width(), sticker->sticker->img->pix(w, h));
} else if (!sticker->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h));
}
if (hover > 0 && _sets[c].id == RecentStickerSetId && _custom.at(index)) {
@ -1411,7 +1411,7 @@ void StickerPanInner::preloadImages() {
if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break;
DocumentData *sticker = _sets.at(i).pack.at(j);
if (!sticker || !sticker->sticker) continue;
if (!sticker || !sticker->sticker()) continue;
bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128));
if (goodThumb) {

View File

@ -2541,7 +2541,7 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
}
AudioData *playing = 0;
AudioMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0;
int32 playingFrequency = 0;
@ -2563,7 +2563,7 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (already || hasdata) {
bool showPause = false;
if (playing == data && playingState != AudioPlayerStopped && playingState != AudioPlayerStoppedAtStart) {
if (playing.msgId == parent->id && playingState != AudioPlayerStopped && playingState != AudioPlayerStoppedAtStart) {
statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
} else {
@ -2728,18 +2728,26 @@ HistoryMedia *HistoryAudio::clone() const {
return new HistoryAudio(*this);
}
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\x94 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title);
}
}
HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia()
, data(document)
, _openl(new DocumentOpenLink(data))
, _savel(new DocumentSaveLink(data))
, _cancell(new DocumentCancelLink(data))
, _name(data->name)
, _name(documentName(data))
, _dldDone(0)
, _uplDone(0)
{
_namew = st::mediaFont->m.width(_name.isEmpty() ? qsl("Document") : _name);
_size = formatSizeText(data->size);
_size = document->song() ? formatDurationAndSizeText(document->song()->duration, data->size) : formatSizeText(data->size);
_height = _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
@ -2800,7 +2808,7 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
if (width < 0) width = w;
if (width < 1) return;
bool out = parent->out(), hovered, pressed;
bool out = parent->out(), hovered, pressed, already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
if (parent == animated.msg) {
int32 pw = animated.w / cIntRetinaFactor(), ph = animated.h / cIntRetinaFactor();
if (width < pw) {
@ -2835,8 +2843,6 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
skipy += fwdFrom;
}
data->thumb->checkload();
if (width >= _maxw) {
width = _maxw;
}
@ -2854,8 +2860,8 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
p.setFont(st::mediaSaveButton.font->f);
QString btnText(lang(data->loader ? lng_media_cancel : (data->already().isEmpty() ? lng_media_download : lng_media_open_with)));
int32 btnTextWidth = data->loader ? _cancelWidth : (data->already().isEmpty() ? _downloadWidth : _openWithWidth);
QString btnText(lang(data->loader ? lng_media_cancel : (already ? lng_media_open_with : lng_media_download)));
int32 btnTextWidth = data->loader ? _cancelWidth : (already ? _openWithWidth : _downloadWidth);
p.drawText(btnx + (btnw - btnTextWidth) / 2, btny + (pressed ? st::mediaSaveButton.downTextTop : st::mediaSaveButton.textTop) + st::mediaSaveButton.font->ascent, btnText);
width -= btnw + st::mediaSaveDelta;
}
@ -2875,10 +2881,76 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
} else if (fwd) {
fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
}
if (_thumbw) {
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
QString statusText;
if (data->song()) {
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0;
int32 playingFrequency = 0;
if (audioPlayer()) {
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
}
QRect img;
if (data->status == FileFailed) {
statusText = lang(lng_attach_failed);
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (data->status == FileUploading) {
if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
_uplDone = data->uploadOffset;
_uplTextCache = formatDownloadText(_uplDone, data->size);
}
statusText = _uplTextCache;
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (already || hasdata) {
bool showPause = false;
if (playing.msgId == parent->id && playingState != AudioPlayerStopped && playingState != AudioPlayerStoppedAtStart) {
statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
} else {
statusText = formatDurationText(data->song()->duration);
}
img = out ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
} else {
if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) {
_dldDone = data->loader->currentOffset();
_dldTextCache = formatDownloadText(_dldDone, data->size);
}
statusText = _dldTextCache;
} else {
statusText = _size;
}
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
}
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img);
} else {
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg));
if (data->status == FileFailed) {
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;
} else if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) {
_dldDone = data->loader->currentOffset();
_dldTextCache = formatDownloadText(_dldDone, data->size);
}
statusText = _dldTextCache;
} else {
statusText = _size;
}
if (_thumbw) {
data->thumb->checkload();
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(), (out ? st::mediaDocOutImg : st::mediaDocInImg));
}
}
if (selected) {
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
@ -2897,28 +2969,9 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name);
}
QString statusText;
style::color status(selected ? (out ? st::mediaOutSelectColor : st::mediaInSelectColor) : (out ? st::mediaOutColor : st::mediaInColor));
p.setPen(status->p);
if (data->status == FileFailed) {
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;
} else if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) {
_dldDone = data->loader->currentOffset();
_dldTextCache = formatDownloadText(_dldDone, data->size);
}
statusText = _dldTextCache;
} else {
statusText = _size;
}
p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, statusText);
p.setFont(st::msgDateFont->f);
@ -2975,11 +3028,11 @@ int32 HistoryDocument::resize(int32 width, bool dontRecountText, const HistoryIt
}
const QString HistoryDocument::inDialogsText() const {
return data->name.isEmpty() ? lang(lng_in_dlg_file) : data->name;
return _name.isEmpty() ? lang(lng_in_dlg_file) : _name;
}
const QString HistoryDocument::inHistoryText() const {
return qsl("[ ") + lang(lng_in_dlg_file) + (data->name.isEmpty() ? QString() : (qsl(" : ") + data->name)) + qsl(" ]");
return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + qsl(" ]");
}
bool HistoryDocument::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
@ -3136,24 +3189,24 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
if (!data->loader && data->status != FileFailed && !already && !hasdata) {
data->save(QString());
}
if (data->sticker->img->isNull() && (already || hasdata)) {
if (data->sticker()->img->isNull() && (already || hasdata)) {
if (already) {
data->sticker->img = ImagePtr(data->already());
data->sticker()->img = ImagePtr(data->already());
} else {
data->sticker->img = ImagePtr(data->data);
data->sticker()->img = ImagePtr(data->data);
}
}
if (selected) {
if (data->sticker->img->isNull()) {
if (data->sticker()->img->isNull()) {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurredColored(st::msgStickerOverlay, pixw, pixh));
} else {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker->img->pixColored(st::msgStickerOverlay, pixw, pixh));
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker()->img->pixColored(st::msgStickerOverlay, pixw, pixh));
}
} else {
if (data->sticker->img->isNull()) {
if (data->sticker()->img->isNull()) {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurred(pixw, pixh));
} else {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker->img->pix(pixw, pixh));
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker()->img->pix(pixw, pixh));
}
}
@ -4785,7 +4838,7 @@ void HistoryMessage::initMediaFromText(QString &currentText) {
}
void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
if (doc->type == StickerDocument && doc->sticker && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->dimensions.width() <= StickerMaxSize && doc->dimensions.height() <= StickerMaxSize && doc->size < StickerInMemory) {
if (doc->sticker()) {
_media = new HistorySticker(doc);
} else {
_media = new HistoryDocument(doc);

View File

@ -839,9 +839,9 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (item && !isUponSelected && !_contextMenuLnk) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg ? msg->getMedia() : 0)) {
DocumentData *doc = sticker->document();
if (doc && doc->sticker && doc->sticker->set.type() != mtpc_inputStickerSetEmpty) {
if (doc && doc->sticker() && doc->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
if (!_menu) _menu = new ContextMenu(this);
_menu->addAction(lang(doc->sticker->setInstalled() ? lng_context_pack_info : lng_context_pack_add), historyWidget, SLOT(onStickerPackInfo()));
_menu->addAction(lang(doc->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), historyWidget, SLOT(onStickerPackInfo()));
}
}
QString contextMenuText = item->selectedText(FullItemSel);
@ -4349,8 +4349,10 @@ namespace {
}
if (document->type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (document->type == StickerDocument && document->sticker) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(document->sticker->alt), document->sticker->set));
} else if (document->type == StickerDocument && document->sticker()) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(document->sticker()->alt), document->sticker()->set));
} else if (document->type == SongDocument && document->song()) {
attributes.push_back(MTP_documentAttributeAudio(MTP_int(document->song()->duration), MTP_string(document->song()->title), MTP_string(document->song()->performer)));
}
return MTP_vector<MTPDocumentAttribute>(attributes);
}
@ -4864,7 +4866,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
App::main()->finishForwarding(hist);
cancelReply(lastKeyboardUsed);
if (sticker->sticker) App::main()->incrementSticker(sticker);
if (sticker->sticker()) App::main()->incrementSticker(sticker);
App::historyRegRandom(randomId, newId);
App::main()->historyToDown(hist);
@ -4973,8 +4975,8 @@ void HistoryWidget::onReplyForwardPreviewCancel() {
void HistoryWidget::onStickerPackInfo() {
if (HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::contextItem())) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(item->getMedia())) {
if (sticker->document() && sticker->document()->sticker && sticker->document()->sticker->set.type() != mtpc_inputStickerSetEmpty) {
App::main()->stickersBox(sticker->document()->sticker->set);
if (sticker->document() && sticker->document()->sticker() && sticker->document()->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
App::main()->stickersBox(sticker->document()->sticker()->set);
}
}
}

View File

@ -18,6 +18,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "localimageloader.h"
#include "gui/filedialog.h"
#include "audio.h"
#include <libexif/exif-data.h>
LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread) : QObject(0)
@ -164,7 +165,40 @@ void LocalImageLoaderPrivate::prepareImages() {
MTPDocument document(MTP_documentEmpty(MTP_long(0)));
MTPAudio audio(MTP_audioEmpty(MTP_long(0)));
bool isSong = false;
QByteArray jpeg;
if (type == ToPrepareDocument) {
if (mime == qstr("audio/mp3") || mime == qstr("audio/m4a") || mime == qstr("audio/aac") || mime == qstr("audio/ogg") ||
filename.endsWith(qstr(".mp3"), Qt::CaseInsensitive) || filename.endsWith(qstr(".m4a"), Qt::CaseInsensitive) ||
filename.endsWith(qstr(".aac"), Qt::CaseInsensitive) || filename.endsWith(qstr(".ogg"), Qt::CaseInsensitive)) {
QImage cover;
QByteArray coverBytes, coverFormat;
MTPDocumentAttribute audioAttribute = audioReadSongAttributes(file, data, cover, coverBytes, coverFormat);
if (audioAttribute.type() == mtpc_documentAttributeAudio) {
attributes.push_back(audioAttribute);
isSong = true;
if (!cover.isNull()) { // cover to thumb
int32 cw = cover.width(), ch = cover.height();
if (cw < 20 * ch && ch < 20 * cw) {
QPixmap full = (cw > 90 || ch > 90) ? QPixmap::fromImage(cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(cover, Qt::ColorOnly);
{
QByteArray thumbFormat = "JPG";
int32 thumbQuality = 87;
QBuffer jpegBuffer(&jpeg);
full.save(&jpegBuffer, thumbFormat, thumbQuality);
}
photoThumbs.insert('0', full);
thumb = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0));
thumbId = MTP::nonce<uint64>();
}
}
}
}
}
if (type == ToPreparePhoto) {
int32 w = img.width(), h = img.height();
@ -189,7 +223,7 @@ void LocalImageLoaderPrivate::prepareImages() {
photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
thumbId = id;
} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) {
} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull() && !isSong) {
int32 w = img.width(), h = img.height();
QByteArray thumbFormat = "JPG";
int32 thumbQuality = 87;

View File

@ -517,6 +517,8 @@ namespace {
typedef QPair<MediaKey, FileLocation> FileLocationPair;
typedef QMap<QString, FileLocationPair> FileLocationPairs;
FileLocationPairs _fileLocationPairs;
typedef QMap<MediaKey, MediaKey> FileLocationAliases;
FileLocationAliases _fileLocationAliases;
FileKey _locationsKey = 0;
FileKey _recentStickersKeyOld = 0, _stickersKey = 0;
@ -566,14 +568,28 @@ namespace {
_writeMap(WriteMapFast);
}
quint32 size = 0;
for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
for (FileLocations::const_iterator i = _fileLocations.cbegin(), e = _fileLocations.cend(); i != e; ++i) {
// location + type + namelen + name + date + size
size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(i.value().name) + _dateTimeSize() + sizeof(quint32);
}
//end mark
size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(QString()) + _dateTimeSize() + sizeof(quint32);
size += sizeof(quint32); // aliases count
for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) {
// alias + location
size += sizeof(quint64) * 2 + sizeof(quint64) * 2;
}
EncryptedDescriptor data(size);
for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(i.value().type) << i.value().name << i.value().modified << quint32(i.value().size);
}
data.stream << quint64(0) << quint64(0) << quint32(0) << QString() << QDateTime::currentDateTime() << quint32(0);
data.stream << quint32(_fileLocationAliases.size());
for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) {
data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second);
}
FileWriteDescriptor file(_locationsKey);
file.writeEncrypted(data);
}
@ -588,12 +604,18 @@ namespace {
return;
}
bool endMarkFound = false;
while (!locations.stream.atEnd()) {
quint64 first, second;
FileLocation loc;
quint32 type;
locations.stream >> first >> second >> type >> loc.name >> loc.modified >> loc.size;
if (!first && !second && !type && loc.name.isEmpty() && !loc.size) { // end mark
endMarkFound = true;
break;
}
MediaKey key(first, second);
loc.type = StorageFileType(type);
@ -604,6 +626,16 @@ namespace {
_writeLocations();
}
}
if (endMarkFound) {
quint32 cnt;
locations.stream >> cnt;
for (int32 i = 0; i < cnt; ++i) {
quint64 kfirst, ksecond, vfirst, vsecond;
locations.stream >> kfirst >> ksecond >> vfirst >> vsecond;
_fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond));
}
}
}
mtpDcOptions *_dcOpts = 0;
@ -1998,12 +2030,21 @@ namespace Local {
return (_draftsPositionsMap.constFind(peer) != _draftsPositionsMap.cend());
}
void writeFileLocation(const MediaKey &location, const FileLocation &local) {
void writeFileLocation(MediaKey location, const FileLocation &local) {
if (local.name.isEmpty()) return;
FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location);
if (aliasIt != _fileLocationAliases.cend()) {
location = aliasIt.value();
}
FileLocationPairs::iterator i = _fileLocationPairs.find(local.name);
if (i != _fileLocationPairs.cend()) {
if (i.value().second == local) {
if (i.value().first != location) {
_fileLocationAliases.insert(location, i.value().first);
_writeLocations(WriteMapFast);
}
return;
}
if (i.value().first != location) {
@ -2021,7 +2062,12 @@ namespace Local {
_writeLocations(WriteMapFast);
}
FileLocation readFileLocation(const MediaKey &location, bool check) {
FileLocation readFileLocation(MediaKey location, bool check) {
FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location);
if (aliasIt != _fileLocationAliases.cend()) {
location = aliasIt.value();
}
FileLocations::iterator i = _fileLocations.find(location);
for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) {
if (check) {
@ -2253,8 +2299,8 @@ namespace Local {
stream << quint64(it->id) << quint64(it->access) << it->title << it->shortName << qint32(it->stickers.size()) << qint32(it->hash) << qint32(it->flags);
for (StickerPack::const_iterator j = it->stickers.cbegin(), e = it->stickers.cend(); j != e; ++j) {
DocumentData *doc = *j;
stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->sticker->alt;
switch (doc->sticker->set.type()) {
stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->sticker()->alt;
switch (doc->sticker()->set.type()) {
case mtpc_inputStickerSetID: {
stream << qint32(StickerSetTypeID);
} break;
@ -2266,7 +2312,7 @@ namespace Local {
stream << qint32(StickerSetTypeEmpty);
} break;
}
const StorageImageLocation &loc(doc->sticker->loc);
const StorageImageLocation &loc(doc->sticker()->loc);
stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret);
}
}
@ -2301,7 +2347,7 @@ namespace Local {
DocumentData *doc = *j;
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker->alt) + sizeof(qint32);
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker()->alt) + sizeof(qint32);
// thumb-width + thumb-height + thumb-dc + thumb-volume + thumb-local + thumb-secret
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64);
@ -2375,7 +2421,7 @@ namespace Local {
}
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, ImagePtr(), dc, size, StorageImageLocation());
if (!doc->sticker) continue;
if (!doc->sticker()) continue;
if (value > 0) {
def.stickers.push_back(doc);
@ -2499,7 +2545,7 @@ namespace Local {
StorageImageLocation thumb(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret);
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.dc ? ImagePtr(thumb) : ImagePtr(), dc, size, thumb);
if (!doc->sticker) continue;
if (!doc->sticker()) continue;
set.stickers.push_back(doc);
++set.count;

View File

@ -113,8 +113,8 @@ namespace Local {
MessageCursor readDraftPositions(const PeerId &peer);
bool hasDraftPositions(const PeerId &peer);
void writeFileLocation(const StorageKey &location, const FileLocation &local);
FileLocation readFileLocation(const StorageKey &location, bool check = true);
void writeFileLocation(MediaKey location, const FileLocation &local);
FileLocation readFileLocation(MediaKey location, bool check = true);
void writeImage(const StorageKey &location, const ImagePtr &img);
void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true);

View File

@ -376,8 +376,10 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
connect(&updateNotifySettingTimer, SIGNAL(timeout()), this, SLOT(onUpdateNotifySettings()));
connect(this, SIGNAL(showPeerAsync(quint64,qint32,bool,bool)), this, SLOT(showPeer(quint64,qint32,bool,bool)), Qt::QueuedConnection);
if (audioPlayer()) {
connect(audioPlayer(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
connect(audioPlayer(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
connect(audioPlayer(), SIGNAL(updated(const AudioMsgId&)), this, SLOT(audioPlayProgress(const AudioMsgId&)));
connect(audioPlayer(), SIGNAL(stopped(const AudioMsgId&)), this, SLOT(audioPlayProgress(const AudioMsgId&)));
connect(audioPlayer(), SIGNAL(updated(const SongMsgId&)), this, SLOT(documentPlayProgress(const SongMsgId&)));
connect(audioPlayer(), SIGNAL(stopped(const SongMsgId&)), this, SLOT(documentPlayProgress(const SongMsgId&)));
}
connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
@ -1409,16 +1411,16 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
if (audio->loader->done()) {
audio->finish();
QString already = audio->already();
bool play = audio->openOnSave > 0 && audioPlayer();
bool play = audio->openOnSave > 0 && audio->openOnSaveMsgId && audioPlayer();
if ((!already.isEmpty() && audio->openOnSave) || (!audio->data.isEmpty() && play)) {
if (play) {
AudioData *playing = 0;
AudioMsgId playing;
AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state);
if (playing == audio && state != AudioPlayerStopped) {
audioPlayer()->pauseresume();
if (playing.msgId == audio->openOnSaveMsgId && state != AudioPlayerStopped) {
audioPlayer()->pauseresume(OverviewAudios);
} else {
audioPlayer()->play(audio);
audioPlayer()->play(AudioMsgId(audio, audio->openOnSaveMsgId));
if (App::main()) App::main()->audioMarkRead(audio);
}
} else {
@ -1442,12 +1444,14 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
}
}
void MainWidget::audioPlayProgress(AudioData *audio) {
AudioData *playing = 0;
void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
AudioMsgId playing;
AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state);
if (playing == audio && state == AudioPlayerStoppedAtStart) {
audioPlayer()->clearStoppedAtStart(audio);
if (playing == audioId && state == AudioPlayerStoppedAtStart) {
audioPlayer()->clearStoppedAtStart(audioId);
AudioData *audio = audioId.audio;
QString already = audio->already(true);
if (already.isEmpty() && !audio->data.isEmpty()) {
bool mp3 = (audio->mime == qstr("audio/mp3"));
@ -1469,12 +1473,53 @@ void MainWidget::audioPlayProgress(AudioData *audio) {
}
}
const AudioItems &items(App::audioItems());
AudioItems::const_iterator i = items.constFind(audio);
if (i != items.cend()) {
for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) {
msgUpdated(j.key()->history()->peer->id, j.key());
if (HistoryItem *item = App::histItemById(audioId.msgId)) {
msgUpdated(item->history()->peer->id, item);
}
}
void MainWidget::documentPlayProgress(const SongMsgId &songId) {
SongMsgId playing;
AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state);
if (playing == songId && state == AudioPlayerStoppedAtStart) {
audioPlayer()->clearStoppedAtStart(songId);
DocumentData *document = songId.song;
QString already = document->already(true);
if (already.isEmpty() && !document->data.isEmpty()) {
QString name = document->name, filter;
MimeType mimeType = mimeTypeForName(document->mime);
QStringList p = mimeType.globPatterns();
QString pattern = p.isEmpty() ? QString() : p.front();
if (name.isEmpty()) {
name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString());
}
if (pattern.isEmpty()) {
filter = qsl("All files (*.*)");
} else {
filter = mimeType.filterString() + qsl(";;All files (*.*)");
}
QString filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, false);
if (!filename.isEmpty()) {
QFile f(filename);
if (f.open(QIODevice::WriteOnly)) {
if (f.write(document->data) == document->data.size()) {
f.close();
already = filename;
document->location = FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename);
Local::writeFileLocation(mediaKey(mtpToLocationType(mtpc_inputDocumentFileLocation), document->dc, document->id), FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename));
}
}
}
}
if (!already.isEmpty()) {
psOpenFile(already);
}
}
if (HistoryItem *item = App::histItemById(songId.msgId)) {
msgUpdated(item->history()->peer->id, item);
}
}
@ -1499,11 +1544,22 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
if (document->loader->done()) {
document->finish();
QString already = document->already();
if (!already.isEmpty() && document->openOnSave) {
if (document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) {
HistoryItem *item = (document->openOnSave && document->openOnSaveMsgId) ? App::histItemById(document->openOnSaveMsgId) : 0;
bool play = document->song() && audioPlayer() && document->openOnSave && item;
if ((!already.isEmpty() || (!document->data.isEmpty() && play)) && document->openOnSave) {
if (play) {
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState);
if (playing.msgId == item->id && playingState != AudioPlayerStopped) {
audioPlayer()->pauseresume(OverviewDocuments);
} else {
audioPlayer()->play(SongMsgId(document, item->id));
}
} else if(document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) {
QImageReader reader(already);
if (reader.canRead()) {
HistoryItem *item = App::histItemById(document->openOnSaveMsgId);
if (reader.supportsAnimation() && reader.imageCount() > 1 && item) {
startGif(item, already);
} else if (item) {
@ -2884,7 +2940,7 @@ void MainWidget::updateNotifySetting(PeerData *peer, bool enabled) {
}
void MainWidget::incrementSticker(DocumentData *sticker) {
if (!sticker || !sticker->sticker) return;
if (!sticker || !sticker->sticker()) return;
RecentStickerPack &recent(cGetRecentStickers());
RecentStickerPack::iterator i = recent.begin(), e = recent.end();
@ -2925,9 +2981,9 @@ void MainWidget::incrementSticker(DocumentData *sticker) {
bool found = false;
uint64 setId = 0;
QString setName;
switch (sticker->sticker->set.type()) {
case mtpc_inputStickerSetID: setId = sticker->sticker->set.c_inputStickerSetID().vid.v; break;
case mtpc_inputStickerSetShortName: setName = qs(sticker->sticker->set.c_inputStickerSetShortName().vshort_name).toLower().trimmed(); break;
switch (sticker->sticker()->set.type()) {
case mtpc_inputStickerSetID: setId = sticker->sticker()->set.c_inputStickerSetID().vid.v; break;
case mtpc_inputStickerSetShortName: setName = qs(sticker->sticker()->set.c_inputStickerSetShortName().vshort_name).toLower().trimmed(); break;
}
StickerSets &sets(cRefStickerSets());
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {

View File

@ -386,10 +386,11 @@ public slots:
void audioLoadProgress(mtpFileLoader *loader);
void audioLoadFailed(mtpFileLoader *loader, bool started);
void audioLoadRetry();
void audioPlayProgress(AudioData *audio);
void audioPlayProgress(const AudioMsgId &audioId);
void documentLoadProgress(mtpFileLoader *loader);
void documentLoadFailed(mtpFileLoader *loader, bool started);
void documentLoadRetry();
void documentPlayProgress(const SongMsgId &songId);
void setInnerFocus();
void dialogsCancelled();

View File

@ -800,9 +800,9 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
_caption = Text();
QString already = _doc->already(true);
if (_doc->sticker && !_doc->sticker->img->isNull() && _doc->sticker->img->loaded()) {
if (_doc->sticker() && !_doc->sticker()->img->isNull() && _doc->sticker()->img->loaded()) {
_currentGif.stop();
_current = _doc->sticker->img->pix();
_current = _doc->sticker()->img->pix();
} else if (!already.isEmpty()) {
QImageReader reader(already);
if (reader.canRead()) {
@ -1010,7 +1010,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
QRect imgRect(_x, _y, _w, _h);
const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.current(_currentGif.w, _currentGif.h, false);
if (imgRect.intersects(r)) {
if (toDraw->hasAlpha() && (!_doc || !_doc->sticker || _doc->sticker->img->isNull())) {
if (toDraw->hasAlpha() && (!_doc || !_doc->sticker() || _doc->sticker()->img->isNull())) {
p.fillRect(imgRect, _transparentBrush);
}
if (_zoom) {
@ -1415,7 +1415,7 @@ void MediaView::preloadData(int32 delta) {
switch (media->type()) {
case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->full->load(); break;
case MediaTypeDocument: static_cast<HistoryDocument*>(media)->document()->thumb->load(); break;
case MediaTypeSticker: static_cast<HistorySticker*>(media)->document()->sticker->img->load(); break;
case MediaTypeSticker: static_cast<HistorySticker*>(media)->document()->sticker()->img->load(); break;
}
}
}

View File

@ -289,7 +289,7 @@ RecentStickerPack &cGetRecentStickers() {
recent.reserve(p.size());
for (RecentStickerPreload::const_iterator i = p.cbegin(), e = p.cend(); i != e; ++i) {
DocumentData *doc = App::document(i->first);
if (!doc || !doc->sticker) continue;
if (!doc || !doc->sticker()) continue;
recent.push_back(qMakePair(doc, i->second));
}

View File

@ -442,16 +442,16 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const {
if ((!data->user && !data->date) || button != Qt::LeftButton) return;
QString already = data->already(true);
bool play = audioPlayer();
bool play = App::hoveredLinkItem() && audioPlayer();
if (!already.isEmpty() || (!data->data.isEmpty() && play)) {
if (play) {
AudioData *playing = 0;
AudioMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState);
if (playing == data && playingState != AudioPlayerStopped) {
audioPlayer()->pauseresume();
if (playing.msgId == App::hoveredLinkItem()->id && playingState != AudioPlayerStopped) {
audioPlayer()->pauseresume(OverviewAudios);
} else {
audioPlayer()->play(data);
audioPlayer()->play(AudioMsgId(data, App::hoveredLinkItem()->id));
if (App::main()) App::main()->audioMarkRead(data);
}
} else {
@ -549,9 +549,19 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const {
DocumentData *data = document();
if (!data->date || button != Qt::LeftButton) return;
bool play = data->song() && App::hoveredLinkItem() && audioPlayer();
QString already = data->already(true);
if (!already.isEmpty()) {
if (data->size < MediaViewImageSizeLimit) {
if (!already.isEmpty() || (!data->data.isEmpty() && play)) {
if (play) {
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState);
if (playing.msgId == App::hoveredLinkItem()->id && playingState != AudioPlayerStopped) {
audioPlayer()->pauseresume(OverviewDocuments);
} else {
audioPlayer()->play(SongMsgId(data, App::hoveredLinkItem()->id));
}
} else if (data->size < MediaViewImageSizeLimit) {
QImageReader reader(already);
if (reader.canRead()) {
if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) {
@ -646,7 +656,7 @@ void DocumentCancelLink::onClick(Qt::MouseButton button) const {
}
DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) :
id(id), type(FileDocument), duration(0), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0), sticker(0) {
id(id), type(FileDocument), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0), _additional(0) {
setattributes(attributes);
location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id));
}
@ -658,12 +668,17 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
const MTPDdocumentAttributeImageSize &d(attributes[i].c_documentAttributeImageSize());
dimensions = QSize(d.vw.v, d.vh.v);
} break;
case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument) type = AnimatedDocument; break;
case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument) {
type = AnimatedDocument;
delete _additional;
_additional = 0;
} break;
case mtpc_documentAttributeSticker: {
const MTPDdocumentAttributeSticker &d(attributes[i].c_documentAttributeSticker());
if (type == FileDocument) type = StickerDocument;
if (type == StickerDocument && !sticker) sticker = new StickerData();
if (sticker) {
if (type == FileDocument) {
type = StickerDocument;
StickerData *sticker = new StickerData();
_additional = sticker;
sticker->alt = qs(d.valt);
sticker->set = d.vstickerset;
}
@ -671,17 +686,28 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
case mtpc_documentAttributeVideo: {
const MTPDdocumentAttributeVideo &d(attributes[i].c_documentAttributeVideo());
type = VideoDocument;
duration = d.vduration.v;
// duration = d.vduration.v;
dimensions = QSize(d.vw.v, d.vh.v);
} break;
case mtpc_documentAttributeAudio: {
const MTPDdocumentAttributeAudio &d(attributes[i].c_documentAttributeAudio());
type = AudioDocument;
duration = d.vduration.v;
type = SongDocument;
SongData *song = new SongData();
_additional = song;
song->duration = d.vduration.v;
song->title = qs(d.vtitle);
song->performer = qs(d.vperformer);
} break;
case mtpc_documentAttributeFilename: name = qs(attributes[i].c_documentAttributeFilename().vfile_name); break;
}
}
if (type == StickerDocument) {
if (dimensions.width() <= 0 || dimensions.height() <= 0 || dimensions.width() > StickerMaxSize || dimensions.height() > StickerMaxSize || size > StickerInMemory) {
type = FileDocument;
delete _additional;
_additional = 0;
}
}
}
void DocumentData::save(const QString &toFile) {

View File

@ -413,6 +413,27 @@ struct AudioData {
int32 md5[8];
};
struct AudioMsgId {
AudioMsgId() : audio(0), msgId(0) {
}
AudioMsgId(AudioData *audio, MsgId msgId) : audio(audio), msgId(msgId) {
}
operator bool() const {
return audio;
}
AudioData *audio;
MsgId msgId;
};
inline bool operator<(const AudioMsgId &a, const AudioMsgId &b) {
return quintptr(a.audio) < quintptr(b.audio) || (quintptr(a.audio) == quintptr(b.audio) && a.msgId < b.msgId);
}
inline bool operator==(const AudioMsgId &a, const AudioMsgId &b) {
return a.audio == b.audio && a.msgId == b.msgId;
}
inline bool operator!=(const AudioMsgId &a, const AudioMsgId &b) {
return !(a == b);
}
class AudioLink : public ITextLink {
TEXT_LINK_CLASS(AudioLink)
@ -455,7 +476,18 @@ public:
void onClick(Qt::MouseButton button) const;
};
struct StickerData {
enum DocumentType {
FileDocument = 0,
VideoDocument = 1,
SongDocument = 2,
StickerDocument = 3,
AnimatedDocument = 4,
};
struct DocumentAdditionalData {
};
struct StickerData : public DocumentAdditionalData {
StickerData() : set(MTP_inputStickerSetEmpty()) {
}
ImagePtr img;
@ -467,20 +499,20 @@ struct StickerData {
StorageImageLocation loc; // doc thumb location
};
enum DocumentType {
FileDocument = 0,
VideoDocument = 1,
AudioDocument = 2,
StickerDocument = 3,
AnimatedDocument = 4,
struct SongData : public DocumentAdditionalData {
SongData() : duration(0) {
}
int32 duration;
QString title, performer;
};
struct DocumentData {
DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
void setattributes(const QVector<MTPDocumentAttribute> &attributes);
void forget() {
thumb->forget();
if (sticker) sticker->img->forget();
if (sticker()) sticker()->img->forget();
replyPreview->forget();
}
@ -510,15 +542,20 @@ struct DocumentData {
loader = 0;
}
~DocumentData() {
delete sticker;
delete _additional;
}
QString already(bool check = false);
StickerData *sticker() {
return (type == StickerDocument) ? static_cast<StickerData*>(_additional) : 0;
}
SongData *song() {
return (type == SongDocument) ? static_cast<SongData*>(_additional) : 0;
}
DocumentId id;
DocumentType type;
QSize dimensions;
int32 duration;
uint64 access;
int32 date;
QString name, mime;
@ -534,11 +571,32 @@ struct DocumentData {
FileLocation location;
QByteArray data;
StickerData *sticker;
DocumentAdditionalData *_additional;
int32 md5[8];
};
struct SongMsgId {
SongMsgId() : song(0), msgId(0) {
}
SongMsgId(DocumentData *song, MsgId msgId) : song(song), msgId(msgId) {
}
operator bool() const {
return song;
}
DocumentData *song;
MsgId msgId;
};
inline bool operator<(const SongMsgId &a, const SongMsgId &b) {
return quintptr(a.song) < quintptr(b.song) || (quintptr(a.song) == quintptr(b.song) && a.msgId < b.msgId);
}
inline bool operator==(const SongMsgId &a, const SongMsgId &b) {
return a.song == b.song && a.msgId == b.msgId;
}
inline bool operator!=(const SongMsgId &a, const SongMsgId &b) {
return !(a == b);
}
class DocumentLink : public ITextLink {
TEXT_LINK_CLASS(DocumentLink)