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); notifySlowHideFunc: transition(easeInCirc);
notifyWaitShortHide: 0; notifyWaitShortHide: 0;
notifyWaitLongHide: 20000; notifyWaitLongHide: 20000;
notifyFastAnim: 100; notifyFastAnim: 150;
notifyFastAnimFunc: transition(linear); notifyFastAnimFunc: transition(linear);
notifyWidth: 316px; notifyWidth: 316px;
notifyHeight: 80px; notifyHeight: 80px;
@ -1968,3 +1968,6 @@ webPagePhotoSize: 100px;
webPagePhotoDelta: 8px; webPagePhotoDelta: 8px;
botDescSkip: 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; int32 wasCount = -1;
for (int32 i = 0, l = d_docs.size(); i != l; ++i) { for (int32 i = 0, l = d_docs.size(); i != l; ++i) {
DocumentData *doc = App::feedDocument(d_docs.at(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 (wasCount < 0) wasCount = it->stickers.size();
if (it->stickers.indexOf(doc) < 0) { if (it->stickers.indexOf(doc) < 0) {

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -45,39 +45,59 @@ public:
AudioPlayer(); AudioPlayer();
void play(AudioData *audio); void play(const AudioMsgId &audio);
void pauseresume(); 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(); void resumeDevice();
~AudioPlayer(); ~AudioPlayer();
public slots: 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: signals:
void updated(AudioData *audio); void updated(const AudioMsgId &audio);
void stopped(AudioData *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 faderOnTimer();
void loaderOnStart(AudioData *audio); void suppressSong();
void loaderOnCancel(AudioData *audio); void unsuppressSong();
void suppressAll();
private: private:
bool updateCurrentStarted(int32 pos = -1); bool startedOther(MediaOverviewType type, bool &fadedStart);
bool updateCurrentStarted(MediaOverviewType type, int32 pos = -1);
struct Msg { 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) { state(AudioPlayerStopped), source(0), nextBuffer(0) {
memset(buffers, 0, sizeof(buffers)); memset(buffers, 0, sizeof(buffers));
memset(samplesCount, 0, sizeof(samplesCount)); memset(samplesCount, 0, sizeof(samplesCount));
} }
AudioData *audio;
QString fname; QString fname;
QByteArray data; QByteArray data;
int64 position, duration; int64 position, duration;
@ -92,9 +112,24 @@ private:
uint32 buffers[3]; uint32 buffers[3];
int64 samplesCount[3]; int64 samplesCount[3];
}; };
struct AudioMsg : public Msg {
AudioMsg() {
}
AudioMsgId audio;
};
struct SongMsg : public Msg {
SongMsg() {
}
SongMsgId song;
};
int32 _current; void currentState(Msg *current, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency);
Msg _data[AudioVoiceMsgSimultaneously];
int32 _audioCurrent;
AudioMsg _audioData[AudioVoiceMsgSimultaneously];
int32 _songCurrent;
SongMsg _songData[AudioSongSimultaneously];
QMutex _mutex; QMutex _mutex;
@ -154,10 +189,14 @@ public:
signals: signals:
void error(AudioData *audio); void error(const AudioMsgId &audio);
void playPositionUpdated(AudioData *audio); void error(const SongMsgId &audio);
void audioStopped(AudioData *audio); void playPositionUpdated(const AudioMsgId &audio);
void needToPreload(AudioData *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(); void stopPauseDevice();
@ -168,12 +207,28 @@ public slots:
void onPauseTimer(); void onPauseTimer();
void onPauseTimerStop(); void onPauseTimerStop();
void onSuppressSong();
void onUnsuppressSong();
void onSuppressAll();
private: 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; QTimer _timer, _pauseTimer;
QMutex _pauseMutex; QMutex _pauseMutex;
bool _pauseFlag, _paused; bool _pauseFlag, _paused;
bool _suppressAll, _suppressAllAnim, _suppressSong, _suppressSongAnim;
anim::fvalue _suppressAllGain, _suppressSongGain;
uint64 _suppressAllStart, _suppressSongStart;
}; };
class AudioPlayerLoader; class AudioPlayerLoader;
@ -187,22 +242,45 @@ public:
signals: signals:
void error(AudioData *audio); void error(const AudioMsgId &audio);
void error(const SongMsgId &song);
void needToCheck(); void needToCheck();
public slots: public slots:
void onInit(); void onInit();
void onStart(AudioData *audio);
void onLoad(AudioData *audio); void onStart(const AudioMsgId &audio);
void onCancel(AudioData *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: private:
typedef QMap<AudioData*, AudioPlayerLoader*> Loaders; AudioMsgId _audio;
Loaders _loaders; 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; 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()); _pack.reserve(v.size());
for (int32 i = 0, l = v.size(); i < l; ++i) { for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i)); DocumentData *doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker) continue; if (!doc || !doc->sticker()) continue;
_pack.push_back(doc); _pack.push_back(doc);
} }
@ -156,11 +156,11 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
if (!doc->loader && doc->status != FileFailed && !already && !hasdata) { if (!doc->loader && doc->status != FileFailed && !already && !hasdata) {
doc->save(QString()); doc->save(QString());
} }
if (doc->sticker->img->isNull() && (already || hasdata)) { if (doc->sticker()->img->isNull() && (already || hasdata)) {
if (already) { if (already) {
doc->sticker->img = ImagePtr(doc->already()); doc->sticker()->img = ImagePtr(doc->already());
} else { } 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); QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (goodThumb) { if (goodThumb) {
p.drawPixmapLeft(ppos, width(), doc->thumb->pix(w, h)); p.drawPixmapLeft(ppos, width(), doc->thumb->pix(w, h));
} else if (!doc->sticker->img->isNull()) { } else if (!doc->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), doc->sticker->img->pix(w, h)); p.drawPixmapLeft(ppos, width(), doc->sticker()->img->pix(w, h));
} }
} }
} }

View File

@ -85,9 +85,10 @@ enum {
MediaOverviewPreloadCount = 4, MediaOverviewPreloadCount = 4,
AudioVoiceMsgSimultaneously = 4, AudioVoiceMsgSimultaneously = 4,
AudioSongSimultaneously = 4,
AudioCheckPositionTimeout = 100, // 100ms per check audio pos AudioCheckPositionTimeout = 100, // 100ms per check audio pos
AudioCheckPositionDelta = 4800, // update position called each 4800 samples AudioCheckPositionDelta = 4800, // update position called each 4800 samples
AudioFadeTimeout = 10, // 10ms AudioFadeTimeout = 7, // 7ms
AudioFadeDuration = 500, AudioFadeDuration = 500,
AudioVoiceMsgSkip = 400, // 200ms AudioVoiceMsgSkip = 400, // 200ms
AudioVoiceMsgFade = 300, // 300ms AudioVoiceMsgFade = 300, // 300ms
@ -100,7 +101,7 @@ enum {
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
AudioPauseDeviceTimeout = 3000, // pause in 3 secs after playing is over 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 StickerMaxSize = 2048, // 2048x2048 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

View File

@ -1216,7 +1216,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
float64 hover = _sets[c].hovers[index]; float64 hover = _sets[c].hovers[index];
DocumentData *sticker = _sets[c].pack[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()); QPoint pos(st::stickerPanPadding + j * st::stickerPanSize.width(), y + i * st::stickerPanSize.height());
if (hover > 0) { if (hover > 0) {
@ -1235,11 +1235,11 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) { if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) {
sticker->save(QString()); sticker->save(QString());
} }
if (sticker->sticker->img->isNull() && (already || hasdata)) { if (sticker->sticker()->img->isNull() && (already || hasdata)) {
if (already) { if (already) {
sticker->sticker->img = ImagePtr(sticker->already()); sticker->sticker()->img = ImagePtr(sticker->already());
} else { } 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); QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
if (goodThumb) { if (goodThumb) {
p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h)); p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h));
} else if (!sticker->sticker->img->isNull()) { } else if (!sticker->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), sticker->sticker->img->pix(w, h)); p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h));
} }
if (hover > 0 && _sets[c].id == RecentStickerSetId && _custom.at(index)) { if (hover > 0 && _sets[c].id == RecentStickerSetId && _custom.at(index)) {
@ -1411,7 +1411,7 @@ void StickerPanInner::preloadImages() {
if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break; if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break;
DocumentData *sticker = _sets.at(i).pack.at(j); 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)); bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128));
if (goodThumb) { 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); fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
} }
AudioData *playing = 0; AudioMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped; AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0; int64 playingPosition = 0, playingDuration = 0;
int32 playingFrequency = 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; img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (already || hasdata) { } else if (already || hasdata) {
bool showPause = false; 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)); statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting); showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
} else { } else {
@ -2728,18 +2728,26 @@ HistoryMedia *HistoryAudio::clone() const {
return new HistoryAudio(*this); 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() HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia()
, data(document) , data(document)
, _openl(new DocumentOpenLink(data)) , _openl(new DocumentOpenLink(data))
, _savel(new DocumentSaveLink(data)) , _savel(new DocumentSaveLink(data))
, _cancell(new DocumentCancelLink(data)) , _cancell(new DocumentCancelLink(data))
, _name(data->name) , _name(documentName(data))
, _dldDone(0) , _dldDone(0)
, _uplDone(0) , _uplDone(0)
{ {
_namew = st::mediaFont->m.width(_name.isEmpty() ? qsl("Document") : _name); _namew = st::mediaFont->m.width(_name.isEmpty() ? qsl("Document") : _name);
_size = document->song() ? formatDurationAndSizeText(document->song()->duration, data->size) : formatSizeText(data->size);
_size = formatSizeText(data->size);
_height = _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); _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 < 0) width = w;
if (width < 1) return; 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) { if (parent == animated.msg) {
int32 pw = animated.w / cIntRetinaFactor(), ph = animated.h / cIntRetinaFactor(); int32 pw = animated.w / cIntRetinaFactor(), ph = animated.h / cIntRetinaFactor();
if (width < pw) { if (width < pw) {
@ -2835,8 +2843,6 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
skipy += fwdFrom; skipy += fwdFrom;
} }
data->thumb->checkload();
if (width >= _maxw) { if (width >= _maxw) {
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.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
p.setFont(st::mediaSaveButton.font->f); p.setFont(st::mediaSaveButton.font->f);
QString btnText(lang(data->loader ? lng_media_cancel : (data->already().isEmpty() ? lng_media_download : lng_media_open_with))); QString btnText(lang(data->loader ? lng_media_cancel : (already ? lng_media_open_with : lng_media_download)));
int32 btnTextWidth = data->loader ? _cancelWidth : (data->already().isEmpty() ? _downloadWidth : _openWithWidth); 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); p.drawText(btnx + (btnw - btnTextWidth) / 2, btny + (pressed ? st::mediaSaveButton.downTextTop : st::mediaSaveButton.textTop) + st::mediaSaveButton.font->ascent, btnText);
width -= btnw + st::mediaSaveDelta; width -= btnw + st::mediaSaveDelta;
} }
@ -2875,10 +2881,76 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
} else if (fwd) { } else if (fwd) {
fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected); 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 { } 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) { if (selected) {
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); 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); 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)); style::color status(selected ? (out ? st::mediaOutSelectColor : st::mediaInSelectColor) : (out ? st::mediaOutColor : st::mediaInColor));
p.setPen(status->p); 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.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, statusText);
p.setFont(st::msgDateFont->f); p.setFont(st::msgDateFont->f);
@ -2975,11 +3028,11 @@ int32 HistoryDocument::resize(int32 width, bool dontRecountText, const HistoryIt
} }
const QString HistoryDocument::inDialogsText() const { 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 { 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 { 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) { if (!data->loader && data->status != FileFailed && !already && !hasdata) {
data->save(QString()); data->save(QString());
} }
if (data->sticker->img->isNull() && (already || hasdata)) { if (data->sticker()->img->isNull() && (already || hasdata)) {
if (already) { if (already) {
data->sticker->img = ImagePtr(data->already()); data->sticker()->img = ImagePtr(data->already());
} else { } else {
data->sticker->img = ImagePtr(data->data); data->sticker()->img = ImagePtr(data->data);
} }
} }
if (selected) { 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)); p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurredColored(st::msgStickerOverlay, pixw, pixh));
} else { } 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 { } 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)); p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurred(pixw, pixh));
} else { } 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) { 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); _media = new HistorySticker(doc);
} else { } else {
_media = new HistoryDocument(doc); _media = new HistoryDocument(doc);

View File

@ -839,9 +839,9 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (item && !isUponSelected && !_contextMenuLnk) { if (item && !isUponSelected && !_contextMenuLnk) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg ? msg->getMedia() : 0)) { if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg ? msg->getMedia() : 0)) {
DocumentData *doc = sticker->document(); 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); 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); QString contextMenuText = item->selectedText(FullItemSel);
@ -4349,8 +4349,10 @@ namespace {
} }
if (document->type == AnimatedDocument) { if (document->type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated()); attributes.push_back(MTP_documentAttributeAnimated());
} else if (document->type == StickerDocument && document->sticker) { } else if (document->type == StickerDocument && document->sticker()) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(document->sticker->alt), document->sticker->set)); 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); return MTP_vector<MTPDocumentAttribute>(attributes);
} }
@ -4864,7 +4866,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
App::main()->finishForwarding(hist); App::main()->finishForwarding(hist);
cancelReply(lastKeyboardUsed); cancelReply(lastKeyboardUsed);
if (sticker->sticker) App::main()->incrementSticker(sticker); if (sticker->sticker()) App::main()->incrementSticker(sticker);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
App::main()->historyToDown(hist); App::main()->historyToDown(hist);
@ -4973,8 +4975,8 @@ void HistoryWidget::onReplyForwardPreviewCancel() {
void HistoryWidget::onStickerPackInfo() { void HistoryWidget::onStickerPackInfo() {
if (HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::contextItem())) { if (HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::contextItem())) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(item->getMedia())) { if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(item->getMedia())) {
if (sticker->document() && sticker->document()->sticker && sticker->document()->sticker->set.type() != mtpc_inputStickerSetEmpty) { if (sticker->document() && sticker->document()->sticker() && sticker->document()->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
App::main()->stickersBox(sticker->document()->sticker->set); 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 "stdafx.h"
#include "localimageloader.h" #include "localimageloader.h"
#include "gui/filedialog.h" #include "gui/filedialog.h"
#include "audio.h"
#include <libexif/exif-data.h> #include <libexif/exif-data.h>
LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread) : QObject(0) LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread) : QObject(0)
@ -164,7 +165,40 @@ void LocalImageLoaderPrivate::prepareImages() {
MTPDocument document(MTP_documentEmpty(MTP_long(0))); MTPDocument document(MTP_documentEmpty(MTP_long(0)));
MTPAudio audio(MTP_audioEmpty(MTP_long(0))); MTPAudio audio(MTP_audioEmpty(MTP_long(0)));
bool isSong = false;
QByteArray jpeg; 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) { if (type == ToPreparePhoto) {
int32 w = img.width(), h = img.height(); 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)); photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
thumbId = id; 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(); int32 w = img.width(), h = img.height();
QByteArray thumbFormat = "JPG"; QByteArray thumbFormat = "JPG";
int32 thumbQuality = 87; int32 thumbQuality = 87;

View File

@ -517,6 +517,8 @@ namespace {
typedef QPair<MediaKey, FileLocation> FileLocationPair; typedef QPair<MediaKey, FileLocation> FileLocationPair;
typedef QMap<QString, FileLocationPair> FileLocationPairs; typedef QMap<QString, FileLocationPair> FileLocationPairs;
FileLocationPairs _fileLocationPairs; FileLocationPairs _fileLocationPairs;
typedef QMap<MediaKey, MediaKey> FileLocationAliases;
FileLocationAliases _fileLocationAliases;
FileKey _locationsKey = 0; FileKey _locationsKey = 0;
FileKey _recentStickersKeyOld = 0, _stickersKey = 0; FileKey _recentStickersKeyOld = 0, _stickersKey = 0;
@ -566,14 +568,28 @@ namespace {
_writeMap(WriteMapFast); _writeMap(WriteMapFast);
} }
quint32 size = 0; 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 // location + type + namelen + name + date + size
size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(i.value().name) + _dateTimeSize() + sizeof(quint32); 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); EncryptedDescriptor data(size);
for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { 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(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); FileWriteDescriptor file(_locationsKey);
file.writeEncrypted(data); file.writeEncrypted(data);
} }
@ -588,12 +604,18 @@ namespace {
return; return;
} }
bool endMarkFound = false;
while (!locations.stream.atEnd()) { while (!locations.stream.atEnd()) {
quint64 first, second; quint64 first, second;
FileLocation loc; FileLocation loc;
quint32 type; quint32 type;
locations.stream >> first >> second >> type >> loc.name >> loc.modified >> loc.size; 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); MediaKey key(first, second);
loc.type = StorageFileType(type); loc.type = StorageFileType(type);
@ -604,6 +626,16 @@ namespace {
_writeLocations(); _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; mtpDcOptions *_dcOpts = 0;
@ -1998,12 +2030,21 @@ namespace Local {
return (_draftsPositionsMap.constFind(peer) != _draftsPositionsMap.cend()); 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; 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); FileLocationPairs::iterator i = _fileLocationPairs.find(local.name);
if (i != _fileLocationPairs.cend()) { if (i != _fileLocationPairs.cend()) {
if (i.value().second == local) { if (i.value().second == local) {
if (i.value().first != location) {
_fileLocationAliases.insert(location, i.value().first);
_writeLocations(WriteMapFast);
}
return; return;
} }
if (i.value().first != location) { if (i.value().first != location) {
@ -2021,7 +2062,12 @@ namespace Local {
_writeLocations(WriteMapFast); _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); FileLocations::iterator i = _fileLocations.find(location);
for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) {
if (check) { 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); 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) { for (StickerPack::const_iterator j = it->stickers.cbegin(), e = it->stickers.cend(); j != e; ++j) {
DocumentData *doc = *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; 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()) { switch (doc->sticker()->set.type()) {
case mtpc_inputStickerSetID: { case mtpc_inputStickerSetID: {
stream << qint32(StickerSetTypeID); stream << qint32(StickerSetTypeID);
} break; } break;
@ -2266,7 +2312,7 @@ namespace Local {
stream << qint32(StickerSetTypeEmpty); stream << qint32(StickerSetTypeEmpty);
} break; } 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); 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; DocumentData *doc = *j;
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set // 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 // 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); 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()); 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) { if (value > 0) {
def.stickers.push_back(doc); def.stickers.push_back(doc);
@ -2499,7 +2545,7 @@ namespace Local {
StorageImageLocation thumb(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret); 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); 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.stickers.push_back(doc);
++set.count; ++set.count;

View File

@ -113,8 +113,8 @@ namespace Local {
MessageCursor readDraftPositions(const PeerId &peer); MessageCursor readDraftPositions(const PeerId &peer);
bool hasDraftPositions(const PeerId &peer); bool hasDraftPositions(const PeerId &peer);
void writeFileLocation(const StorageKey &location, const FileLocation &local); void writeFileLocation(MediaKey location, const FileLocation &local);
FileLocation readFileLocation(const StorageKey &location, bool check = true); FileLocation readFileLocation(MediaKey location, bool check = true);
void writeImage(const StorageKey &location, const ImagePtr &img); void writeImage(const StorageKey &location, const ImagePtr &img);
void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true); 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(&updateNotifySettingTimer, SIGNAL(timeout()), this, SLOT(onUpdateNotifySettings()));
connect(this, SIGNAL(showPeerAsync(quint64,qint32,bool,bool)), this, SLOT(showPeer(quint64,qint32,bool,bool)), Qt::QueuedConnection); connect(this, SIGNAL(showPeerAsync(quint64,qint32,bool,bool)), this, SLOT(showPeer(quint64,qint32,bool,bool)), Qt::QueuedConnection);
if (audioPlayer()) { if (audioPlayer()) {
connect(audioPlayer(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*))); connect(audioPlayer(), SIGNAL(updated(const AudioMsgId&)), this, SLOT(audioPlayProgress(const AudioMsgId&)));
connect(audioPlayer(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*))); 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())); connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
@ -1409,16 +1411,16 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
if (audio->loader->done()) { if (audio->loader->done()) {
audio->finish(); audio->finish();
QString already = audio->already(); 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 ((!already.isEmpty() && audio->openOnSave) || (!audio->data.isEmpty() && play)) {
if (play) { if (play) {
AudioData *playing = 0; AudioMsgId playing;
AudioPlayerState state = AudioPlayerStopped; AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state); audioPlayer()->currentState(&playing, &state);
if (playing == audio && state != AudioPlayerStopped) { if (playing.msgId == audio->openOnSaveMsgId && state != AudioPlayerStopped) {
audioPlayer()->pauseresume(); audioPlayer()->pauseresume(OverviewAudios);
} else { } else {
audioPlayer()->play(audio); audioPlayer()->play(AudioMsgId(audio, audio->openOnSaveMsgId));
if (App::main()) App::main()->audioMarkRead(audio); if (App::main()) App::main()->audioMarkRead(audio);
} }
} else { } else {
@ -1442,12 +1444,14 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
} }
} }
void MainWidget::audioPlayProgress(AudioData *audio) { void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
AudioData *playing = 0; AudioMsgId playing;
AudioPlayerState state = AudioPlayerStopped; AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state); audioPlayer()->currentState(&playing, &state);
if (playing == audio && state == AudioPlayerStoppedAtStart) { if (playing == audioId && state == AudioPlayerStoppedAtStart) {
audioPlayer()->clearStoppedAtStart(audio); audioPlayer()->clearStoppedAtStart(audioId);
AudioData *audio = audioId.audio;
QString already = audio->already(true); QString already = audio->already(true);
if (already.isEmpty() && !audio->data.isEmpty()) { if (already.isEmpty() && !audio->data.isEmpty()) {
bool mp3 = (audio->mime == qstr("audio/mp3")); bool mp3 = (audio->mime == qstr("audio/mp3"));
@ -1469,12 +1473,53 @@ void MainWidget::audioPlayProgress(AudioData *audio) {
} }
} }
const AudioItems &items(App::audioItems()); if (HistoryItem *item = App::histItemById(audioId.msgId)) {
AudioItems::const_iterator i = items.constFind(audio); msgUpdated(item->history()->peer->id, item);
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());
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()) { if (document->loader->done()) {
document->finish(); document->finish();
QString already = document->already(); 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); QImageReader reader(already);
if (reader.canRead()) { if (reader.canRead()) {
HistoryItem *item = App::histItemById(document->openOnSaveMsgId);
if (reader.supportsAnimation() && reader.imageCount() > 1 && item) { if (reader.supportsAnimation() && reader.imageCount() > 1 && item) {
startGif(item, already); startGif(item, already);
} else if (item) { } else if (item) {
@ -2884,7 +2940,7 @@ void MainWidget::updateNotifySetting(PeerData *peer, bool enabled) {
} }
void MainWidget::incrementSticker(DocumentData *sticker) { void MainWidget::incrementSticker(DocumentData *sticker) {
if (!sticker || !sticker->sticker) return; if (!sticker || !sticker->sticker()) return;
RecentStickerPack &recent(cGetRecentStickers()); RecentStickerPack &recent(cGetRecentStickers());
RecentStickerPack::iterator i = recent.begin(), e = recent.end(); RecentStickerPack::iterator i = recent.begin(), e = recent.end();
@ -2925,9 +2981,9 @@ void MainWidget::incrementSticker(DocumentData *sticker) {
bool found = false; bool found = false;
uint64 setId = 0; uint64 setId = 0;
QString setName; QString setName;
switch (sticker->sticker->set.type()) { switch (sticker->sticker()->set.type()) {
case mtpc_inputStickerSetID: setId = sticker->sticker->set.c_inputStickerSetID().vid.v; break; 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; case mtpc_inputStickerSetShortName: setName = qs(sticker->sticker()->set.c_inputStickerSetShortName().vshort_name).toLower().trimmed(); break;
} }
StickerSets &sets(cRefStickerSets()); StickerSets &sets(cRefStickerSets());
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) { for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {

View File

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

View File

@ -800,9 +800,9 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
_caption = Text(); _caption = Text();
QString already = _doc->already(true); 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(); _currentGif.stop();
_current = _doc->sticker->img->pix(); _current = _doc->sticker()->img->pix();
} else if (!already.isEmpty()) { } else if (!already.isEmpty()) {
QImageReader reader(already); QImageReader reader(already);
if (reader.canRead()) { if (reader.canRead()) {
@ -1010,7 +1010,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
QRect imgRect(_x, _y, _w, _h); QRect imgRect(_x, _y, _w, _h);
const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.current(_currentGif.w, _currentGif.h, false); const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.current(_currentGif.w, _currentGif.h, false);
if (imgRect.intersects(r)) { 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); p.fillRect(imgRect, _transparentBrush);
} }
if (_zoom) { if (_zoom) {
@ -1415,7 +1415,7 @@ void MediaView::preloadData(int32 delta) {
switch (media->type()) { switch (media->type()) {
case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->full->load(); break; case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->full->load(); break;
case MediaTypeDocument: static_cast<HistoryDocument*>(media)->document()->thumb->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()); recent.reserve(p.size());
for (RecentStickerPreload::const_iterator i = p.cbegin(), e = p.cend(); i != e; ++i) { for (RecentStickerPreload::const_iterator i = p.cbegin(), e = p.cend(); i != e; ++i) {
DocumentData *doc = App::document(i->first); DocumentData *doc = App::document(i->first);
if (!doc || !doc->sticker) continue; if (!doc || !doc->sticker()) continue;
recent.push_back(qMakePair(doc, i->second)); 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; if ((!data->user && !data->date) || button != Qt::LeftButton) return;
QString already = data->already(true); QString already = data->already(true);
bool play = audioPlayer(); bool play = App::hoveredLinkItem() && audioPlayer();
if (!already.isEmpty() || (!data->data.isEmpty() && play)) { if (!already.isEmpty() || (!data->data.isEmpty() && play)) {
if (play) { if (play) {
AudioData *playing = 0; AudioMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped; AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState); audioPlayer()->currentState(&playing, &playingState);
if (playing == data && playingState != AudioPlayerStopped) { if (playing.msgId == App::hoveredLinkItem()->id && playingState != AudioPlayerStopped) {
audioPlayer()->pauseresume(); audioPlayer()->pauseresume(OverviewAudios);
} else { } else {
audioPlayer()->play(data); audioPlayer()->play(AudioMsgId(data, App::hoveredLinkItem()->id));
if (App::main()) App::main()->audioMarkRead(data); if (App::main()) App::main()->audioMarkRead(data);
} }
} else { } else {
@ -549,9 +549,19 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const {
DocumentData *data = document(); DocumentData *data = document();
if (!data->date || button != Qt::LeftButton) return; if (!data->date || button != Qt::LeftButton) return;
bool play = data->song() && App::hoveredLinkItem() && audioPlayer();
QString already = data->already(true); QString already = data->already(true);
if (!already.isEmpty()) { if (!already.isEmpty() || (!data->data.isEmpty() && play)) {
if (data->size < MediaViewImageSizeLimit) { 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); QImageReader reader(already);
if (reader.canRead()) { if (reader.canRead()) {
if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) { 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) : 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); setattributes(attributes);
location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); 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()); const MTPDdocumentAttributeImageSize &d(attributes[i].c_documentAttributeImageSize());
dimensions = QSize(d.vw.v, d.vh.v); dimensions = QSize(d.vw.v, d.vh.v);
} break; } 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: { case mtpc_documentAttributeSticker: {
const MTPDdocumentAttributeSticker &d(attributes[i].c_documentAttributeSticker()); const MTPDdocumentAttributeSticker &d(attributes[i].c_documentAttributeSticker());
if (type == FileDocument) type = StickerDocument; if (type == FileDocument) {
if (type == StickerDocument && !sticker) sticker = new StickerData(); type = StickerDocument;
if (sticker) { StickerData *sticker = new StickerData();
_additional = sticker;
sticker->alt = qs(d.valt); sticker->alt = qs(d.valt);
sticker->set = d.vstickerset; sticker->set = d.vstickerset;
} }
@ -671,17 +686,28 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
case mtpc_documentAttributeVideo: { case mtpc_documentAttributeVideo: {
const MTPDdocumentAttributeVideo &d(attributes[i].c_documentAttributeVideo()); const MTPDdocumentAttributeVideo &d(attributes[i].c_documentAttributeVideo());
type = VideoDocument; type = VideoDocument;
duration = d.vduration.v; // duration = d.vduration.v;
dimensions = QSize(d.vw.v, d.vh.v); dimensions = QSize(d.vw.v, d.vh.v);
} break; } break;
case mtpc_documentAttributeAudio: { case mtpc_documentAttributeAudio: {
const MTPDdocumentAttributeAudio &d(attributes[i].c_documentAttributeAudio()); const MTPDdocumentAttributeAudio &d(attributes[i].c_documentAttributeAudio());
type = AudioDocument; type = SongDocument;
duration = d.vduration.v; SongData *song = new SongData();
_additional = song;
song->duration = d.vduration.v;
song->title = qs(d.vtitle);
song->performer = qs(d.vperformer);
} break; } break;
case mtpc_documentAttributeFilename: name = qs(attributes[i].c_documentAttributeFilename().vfile_name); 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) { void DocumentData::save(const QString &toFile) {

View File

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