mirror of
				https://github.com/kotatogram/kotatogram-desktop
				synced 2025-10-29 15:36:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			417 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			417 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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()
 | |
| 			|| hasMimeType(qstr("image/gif"));
 | |
| 	}
 | |
| 	bool isGifv() const {
 | |
| 		return (type == AnimatedDocument)
 | |
| 			&& hasMimeType(qstr("video/mp4"));
 | |
| 	}
 | |
| 	bool isTheme() const {
 | |
| 		return
 | |
| 			_filename.endsWith(
 | |
| 				qstr(".tdesktop-theme"),
 | |
| 				Qt::CaseInsensitive)
 | |
| 			|| _filename.endsWith(
 | |
| 				qstr(".tdesktop-palette"),
 | |
| 				Qt::CaseInsensitive);
 | |
| 	}
 | |
| 	bool tryPlaySong() const {
 | |
| 		return (song() != nullptr)
 | |
| 			|| _mimeString.startsWith(
 | |
| 				qstr("audio/"),
 | |
| 				Qt::CaseInsensitive);
 | |
| 	}
 | |
| 	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);
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	~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);
 | |
| 	}
 | |
| 
 | |
| 	static QString ComposeNameString(
 | |
| 		const QString &filename,
 | |
| 		const QString &songTitle,
 | |
| 		const QString &songPerformer);
 | |
| 	QString composeNameString() const {
 | |
| 		if (auto songData = song()) {
 | |
| 			return ComposeNameString(
 | |
| 				_filename,
 | |
| 				songData->title,
 | |
| 				songData->performer);
 | |
| 		}
 | |
| 		return ComposeNameString(_filename, QString(), QString());
 | |
| 	}
 | |
| 
 | |
| 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;
 | |
| 	QString _filename;
 | |
| 	QString _mimeString;
 | |
| 
 | |
| 	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());
 |