2017-09-26 11:49:16 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "data/data_types.h"
|
|
|
|
|
|
|
|
inline uint64 mediaMix32To64(int32 a, int32 b) {
|
|
|
|
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32)
|
|
|
|
| uint64(*reinterpret_cast<uint32*>(&b));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Old method, should not be used anymore.
|
|
|
|
//inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id) {
|
|
|
|
// return MediaKey(mediaMix32To64(type, dc), id);
|
|
|
|
//}
|
|
|
|
// New method when version was introduced, type is not relevant anymore (all files are Documents).
|
|
|
|
inline MediaKey mediaKey(
|
|
|
|
LocationType type,
|
|
|
|
int32 dc,
|
|
|
|
const uint64 &id,
|
|
|
|
int32 version) {
|
|
|
|
return (version > 0) ? MediaKey(mediaMix32To64(version, dc), id) : MediaKey(mediaMix32To64(type, dc), id);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline StorageKey mediaKey(const MTPDfileLocation &location) {
|
|
|
|
return storageKey(
|
|
|
|
location.vdc_id.v,
|
|
|
|
location.vvolume_id.v,
|
|
|
|
location.vlocal_id.v);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DocumentAdditionalData {
|
|
|
|
virtual ~DocumentAdditionalData() = default;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
struct StickerData : public DocumentAdditionalData {
|
|
|
|
ImagePtr img;
|
|
|
|
QString alt;
|
|
|
|
|
|
|
|
MTPInputStickerSet set = MTP_inputStickerSetEmpty();
|
|
|
|
bool setInstalled() const;
|
|
|
|
|
|
|
|
StorageImageLocation loc; // doc thumb location
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SongData : public DocumentAdditionalData {
|
|
|
|
int32 duration = 0;
|
|
|
|
QString title, performer;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VoiceData : public DocumentAdditionalData {
|
|
|
|
~VoiceData();
|
|
|
|
|
|
|
|
int duration = 0;
|
|
|
|
VoiceWaveform waveform;
|
|
|
|
char wavemax = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool fileIsImage(const QString &name, const QString &mime);
|
|
|
|
|
|
|
|
namespace Serialize {
|
|
|
|
class Document;
|
|
|
|
} // namespace Serialize;
|
|
|
|
|
|
|
|
class DocumentData {
|
|
|
|
public:
|
|
|
|
static DocumentData *create(DocumentId id);
|
|
|
|
static DocumentData *create(
|
|
|
|
DocumentId id,
|
|
|
|
int32 dc,
|
|
|
|
uint64 accessHash,
|
|
|
|
int32 version,
|
|
|
|
const QVector<MTPDocumentAttribute> &attributes);
|
|
|
|
static DocumentData *create(
|
|
|
|
DocumentId id,
|
|
|
|
const QString &url,
|
|
|
|
const QVector<MTPDocumentAttribute> &attributes);
|
|
|
|
|
|
|
|
void setattributes(
|
|
|
|
const QVector<MTPDocumentAttribute> &attributes);
|
|
|
|
|
|
|
|
void automaticLoad(const HistoryItem *item); // auto load sticker or video
|
|
|
|
void automaticLoadSettingsChanged();
|
|
|
|
|
|
|
|
enum FilePathResolveType {
|
|
|
|
FilePathResolveCached,
|
|
|
|
FilePathResolveChecked,
|
|
|
|
FilePathResolveSaveFromData,
|
|
|
|
FilePathResolveSaveFromDataSilent,
|
|
|
|
};
|
|
|
|
bool loaded(
|
|
|
|
FilePathResolveType type = FilePathResolveCached) const;
|
|
|
|
bool loading() const;
|
|
|
|
QString loadingFilePath() const;
|
|
|
|
bool displayLoading() const;
|
|
|
|
void save(
|
|
|
|
const QString &toFile,
|
|
|
|
ActionOnLoad action = ActionOnLoadNone,
|
|
|
|
const FullMsgId &actionMsgId = FullMsgId(),
|
|
|
|
LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal,
|
|
|
|
bool autoLoading = false);
|
|
|
|
void cancel();
|
|
|
|
float64 progress() const;
|
|
|
|
int32 loadOffset() const;
|
|
|
|
bool uploading() const;
|
|
|
|
|
|
|
|
QByteArray data() const;
|
|
|
|
const FileLocation &location(bool check = false) const;
|
|
|
|
void setLocation(const FileLocation &loc);
|
|
|
|
|
|
|
|
QString filepath(
|
|
|
|
FilePathResolveType type = FilePathResolveCached,
|
|
|
|
bool forceSavingAs = false) const;
|
|
|
|
|
|
|
|
bool saveToCache() const;
|
|
|
|
|
|
|
|
void performActionOnLoad();
|
|
|
|
|
|
|
|
void forget();
|
|
|
|
ImagePtr makeReplyPreview();
|
|
|
|
|
|
|
|
StickerData *sticker() {
|
|
|
|
return (type == StickerDocument)
|
|
|
|
? static_cast<StickerData*>(_additional.get())
|
|
|
|
: nullptr;
|
|
|
|
}
|
|
|
|
void checkSticker() {
|
|
|
|
StickerData *s = sticker();
|
|
|
|
if (!s) return;
|
|
|
|
|
|
|
|
automaticLoad(nullptr);
|
|
|
|
if (s->img->isNull() && loaded()) {
|
|
|
|
if (_data.isEmpty()) {
|
|
|
|
const FileLocation &loc(location(true));
|
|
|
|
if (loc.accessEnable()) {
|
|
|
|
s->img = ImagePtr(loc.name());
|
|
|
|
loc.accessDisable();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s->img = ImagePtr(_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SongData *song() {
|
|
|
|
return (type == SongDocument)
|
|
|
|
? static_cast<SongData*>(_additional.get())
|
|
|
|
: nullptr;
|
|
|
|
}
|
|
|
|
const SongData *song() const {
|
|
|
|
return const_cast<DocumentData*>(this)->song();
|
|
|
|
}
|
|
|
|
VoiceData *voice() {
|
|
|
|
return (type == VoiceDocument)
|
|
|
|
? static_cast<VoiceData*>(_additional.get())
|
|
|
|
: nullptr;
|
|
|
|
}
|
|
|
|
const VoiceData *voice() const {
|
|
|
|
return const_cast<DocumentData*>(this)->voice();
|
|
|
|
}
|
|
|
|
bool isRoundVideo() const {
|
|
|
|
return (type == RoundVideoDocument);
|
|
|
|
}
|
|
|
|
bool isAnimation() const {
|
|
|
|
return (type == AnimatedDocument)
|
|
|
|
|| isRoundVideo()
|
2017-11-05 11:00:48 +00:00
|
|
|
|| hasMimeType(qstr("image/gif"));
|
2017-09-26 11:49:16 +00:00
|
|
|
}
|
|
|
|
bool isGifv() const {
|
|
|
|
return (type == AnimatedDocument)
|
2017-11-05 11:00:48 +00:00
|
|
|
&& hasMimeType(qstr("video/mp4"));
|
2017-09-26 11:49:16 +00:00
|
|
|
}
|
|
|
|
bool isTheme() const {
|
|
|
|
return
|
2017-11-05 11:00:48 +00:00
|
|
|
_filename.endsWith(
|
2017-09-26 11:49:16 +00:00
|
|
|
qstr(".tdesktop-theme"),
|
|
|
|
Qt::CaseInsensitive)
|
2017-11-05 11:00:48 +00:00
|
|
|
|| _filename.endsWith(
|
2017-09-26 11:49:16 +00:00
|
|
|
qstr(".tdesktop-palette"),
|
|
|
|
Qt::CaseInsensitive);
|
|
|
|
}
|
|
|
|
bool tryPlaySong() const {
|
|
|
|
return (song() != nullptr)
|
2017-11-05 11:00:48 +00:00
|
|
|
|| _mimeString.startsWith(
|
|
|
|
qstr("audio/"),
|
|
|
|
Qt::CaseInsensitive);
|
2017-09-26 11:49:16 +00:00
|
|
|
}
|
|
|
|
bool isMusic() const {
|
|
|
|
if (auto s = song()) {
|
|
|
|
return (s->duration > 0);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool isVideo() const {
|
|
|
|
return (type == VideoDocument);
|
|
|
|
}
|
|
|
|
int32 duration() const {
|
|
|
|
return (isAnimation() || isVideo()) ? _duration : -1;
|
|
|
|
}
|
|
|
|
bool isImage() const {
|
|
|
|
return !isAnimation() && !isVideo() && (_duration > 0);
|
|
|
|
}
|
|
|
|
void recountIsImage();
|
|
|
|
void setData(const QByteArray &data) {
|
|
|
|
_data = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool setRemoteVersion(int32 version); // Returns true if version has changed.
|
|
|
|
void setRemoteLocation(int32 dc, uint64 access);
|
|
|
|
void setContentUrl(const QString &url);
|
|
|
|
bool hasRemoteLocation() const {
|
|
|
|
return (_dc != 0 && _access != 0);
|
|
|
|
}
|
|
|
|
bool isValid() const {
|
|
|
|
return hasRemoteLocation() || !_url.isEmpty();
|
|
|
|
}
|
|
|
|
MTPInputDocument mtpInput() const {
|
|
|
|
if (_access) {
|
|
|
|
return MTP_inputDocument(
|
|
|
|
MTP_long(id),
|
|
|
|
MTP_long(_access));
|
|
|
|
}
|
|
|
|
return MTP_inputDocumentEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
// When we have some client-side generated document
|
|
|
|
// (for example for displaying an external inline bot result)
|
|
|
|
// and it has downloaded data, we can collect that data from it
|
|
|
|
// to (this) received from the server "same" document.
|
|
|
|
void collectLocalData(DocumentData *local);
|
|
|
|
|
2017-11-05 11:00:48 +00:00
|
|
|
QString filename() const {
|
|
|
|
return _filename;
|
|
|
|
}
|
|
|
|
QString mimeString() const {
|
|
|
|
return _mimeString;
|
|
|
|
}
|
|
|
|
bool hasMimeType(QLatin1String mime) const {
|
|
|
|
return !_mimeString.compare(mime, Qt::CaseInsensitive);
|
|
|
|
}
|
|
|
|
void setMimeString(const QString &mime) {
|
|
|
|
_mimeString = mime;
|
|
|
|
}
|
|
|
|
|
2017-09-26 11:49:16 +00:00
|
|
|
~DocumentData();
|
|
|
|
|
|
|
|
DocumentId id = 0;
|
|
|
|
DocumentType type = FileDocument;
|
|
|
|
QSize dimensions;
|
|
|
|
int32 date = 0;
|
|
|
|
ImagePtr thumb, replyPreview;
|
|
|
|
int32 size = 0;
|
|
|
|
|
|
|
|
FileStatus status = FileReady;
|
|
|
|
int32 uploadOffset = 0;
|
|
|
|
|
|
|
|
int32 md5[8];
|
|
|
|
|
|
|
|
MediaKey mediaKey() const {
|
|
|
|
return ::mediaKey(locationType(), _dc, id, _version);
|
|
|
|
}
|
|
|
|
|
2017-11-05 11:00:48 +00:00
|
|
|
static QString ComposeNameString(
|
2017-09-26 11:49:16 +00:00
|
|
|
const QString &filename,
|
|
|
|
const QString &songTitle,
|
|
|
|
const QString &songPerformer);
|
|
|
|
QString composeNameString() const {
|
|
|
|
if (auto songData = song()) {
|
2017-11-05 11:00:48 +00:00
|
|
|
return ComposeNameString(
|
|
|
|
_filename,
|
2017-09-26 11:49:16 +00:00
|
|
|
songData->title,
|
|
|
|
songData->performer);
|
|
|
|
}
|
2017-11-05 11:00:48 +00:00
|
|
|
return ComposeNameString(_filename, QString(), QString());
|
2017-09-26 11:49:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DocumentData(
|
|
|
|
DocumentId id,
|
|
|
|
int32 dc,
|
|
|
|
uint64 accessHash,
|
|
|
|
int32 version,
|
|
|
|
const QString &url,
|
|
|
|
const QVector<MTPDocumentAttribute> &attributes);
|
|
|
|
|
|
|
|
friend class Serialize::Document;
|
|
|
|
|
|
|
|
LocationType locationType() const {
|
|
|
|
return voice()
|
|
|
|
? AudioFileLocation
|
|
|
|
: isVideo()
|
|
|
|
? VideoFileLocation
|
|
|
|
: DocumentFileLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Two types of location: from MTProto by dc+access+version or from web by url
|
|
|
|
int32 _dc = 0;
|
|
|
|
uint64 _access = 0;
|
|
|
|
int32 _version = 0;
|
|
|
|
QString _url;
|
2017-11-05 11:00:48 +00:00
|
|
|
QString _filename;
|
|
|
|
QString _mimeString;
|
2017-09-26 11:49:16 +00:00
|
|
|
|
|
|
|
FileLocation _location;
|
|
|
|
QByteArray _data;
|
|
|
|
std::unique_ptr<DocumentAdditionalData> _additional;
|
|
|
|
int32 _duration = -1;
|
|
|
|
|
|
|
|
ActionOnLoad _actionOnLoad = ActionOnLoadNone;
|
|
|
|
FullMsgId _actionOnLoadMsgId;
|
|
|
|
mutable FileLoader *_loader = nullptr;
|
|
|
|
|
|
|
|
void notifyLayoutChanged() const;
|
|
|
|
|
|
|
|
void destroyLoaderDelayed(
|
|
|
|
mtpFileLoader *newValue = nullptr) const;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);
|
|
|
|
QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform);
|
|
|
|
|
|
|
|
class DocumentClickHandler : public LeftButtonClickHandler {
|
|
|
|
public:
|
|
|
|
DocumentClickHandler(DocumentData *document)
|
|
|
|
: _document(document) {
|
|
|
|
}
|
|
|
|
DocumentData *document() const {
|
|
|
|
return _document;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DocumentData *_document;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class DocumentSaveClickHandler : public DocumentClickHandler {
|
|
|
|
public:
|
|
|
|
using DocumentClickHandler::DocumentClickHandler;
|
|
|
|
static void doSave(
|
|
|
|
DocumentData *document,
|
|
|
|
bool forceSavingAs = false);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void onClickImpl() const override;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class DocumentOpenClickHandler : public DocumentClickHandler {
|
|
|
|
public:
|
|
|
|
using DocumentClickHandler::DocumentClickHandler;
|
|
|
|
static void doOpen(
|
|
|
|
DocumentData *document,
|
|
|
|
HistoryItem *context,
|
|
|
|
ActionOnLoad action = ActionOnLoadOpen);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void onClickImpl() const override;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class DocumentCancelClickHandler : public DocumentClickHandler {
|
|
|
|
public:
|
|
|
|
using DocumentClickHandler::DocumentClickHandler;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void onClickImpl() const override;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class GifOpenClickHandler : public DocumentOpenClickHandler {
|
|
|
|
public:
|
|
|
|
using DocumentOpenClickHandler::DocumentOpenClickHandler;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void onClickImpl() const override;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class VoiceSeekClickHandler : public DocumentOpenClickHandler {
|
|
|
|
public:
|
|
|
|
using DocumentOpenClickHandler::DocumentOpenClickHandler;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void onClickImpl() const override {
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
QString saveFileName(
|
|
|
|
const QString &title,
|
|
|
|
const QString &filter,
|
|
|
|
const QString &prefix,
|
|
|
|
QString name,
|
|
|
|
bool savingAs,
|
|
|
|
const QDir &dir = QDir());
|