1748 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1748 lines
		
	
	
	
		
			44 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.
 | |
| 
 | |
| Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | |
| Copyright (c) 2014 John Preston, https://desktop.telegram.org
 | |
| */
 | |
| #pragma once
 | |
| 
 | |
| typedef uint64 PeerId;
 | |
| typedef uint64 PhotoId;
 | |
| typedef uint64 VideoId;
 | |
| typedef uint64 AudioId;
 | |
| typedef uint64 DocumentId;
 | |
| typedef int32 MsgId;
 | |
| 
 | |
| void historyInit();
 | |
| 
 | |
| class HistoryItem;
 | |
| 
 | |
| void startGif(HistoryItem *row, const QString &file);
 | |
| void itemRemovedGif(HistoryItem *item);
 | |
| void itemReplacedGif(HistoryItem *oldItem, HistoryItem *newItem);
 | |
| void stopGif();
 | |
| 
 | |
| static const uint32 FullItemSel = 0xFFFFFFFF;
 | |
| 
 | |
| typedef QMap<int32, HistoryItem*> SelectedItemSet;
 | |
| 
 | |
| extern TextParseOptions _textNameOptions, _textDlgOptions;
 | |
| 
 | |
| struct NotifySettings {
 | |
| 	NotifySettings() : mute(0), sound("default"), previews(true), events(1) {
 | |
| 	}
 | |
| 	int32 mute;
 | |
| 	string sound;
 | |
| 	bool previews;
 | |
| 	int32 events;
 | |
| };
 | |
| typedef NotifySettings *NotifySettingsPtr;
 | |
| 
 | |
| static const NotifySettingsPtr UnknownNotifySettings = NotifySettingsPtr(0);
 | |
| static const NotifySettingsPtr EmptyNotifySettings = NotifySettingsPtr(1);
 | |
| extern NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats;
 | |
| extern NotifySettingsPtr globalNotifyAllPtr, globalNotifyUsersPtr, globalNotifyChatsPtr;
 | |
| 
 | |
| inline bool isNotifyMuted(NotifySettingsPtr settings) {
 | |
| 	if (settings == UnknownNotifySettings || settings == EmptyNotifySettings) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	return (settings->mute > unixtime());
 | |
| }
 | |
| 
 | |
| style::color peerColor(int32 index);
 | |
| ImagePtr userDefPhoto(int32 index);
 | |
| ImagePtr chatDefPhoto(int32 index);
 | |
| 
 | |
| struct ChatData;
 | |
| struct UserData;
 | |
| struct PeerData {
 | |
| 	PeerData(const PeerId &id);
 | |
| 	virtual ~PeerData() {
 | |
| 		if (notify != UnknownNotifySettings && notify != EmptyNotifySettings) {
 | |
| 			delete notify;
 | |
| 			notify = UnknownNotifySettings;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	UserData *asUser();
 | |
| 	const UserData *asUser() const;
 | |
| 
 | |
| 	ChatData *asChat();
 | |
| 	const ChatData *asChat() const;
 | |
| 
 | |
| 	void updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername);
 | |
| 
 | |
| 	void fillNames();
 | |
| 
 | |
| 	virtual void nameUpdated() {
 | |
| 	}
 | |
| 
 | |
| 	PeerId id;
 | |
| 
 | |
| 	QString name;
 | |
| 	QString nameOrPhone;
 | |
| 	typedef QSet<QString> Names;
 | |
| 	Names names; // for filtering
 | |
| 	typedef QSet<QChar> NameFirstChars;
 | |
| 	NameFirstChars chars;
 | |
| 
 | |
| 	bool loaded;
 | |
| 	bool chat;
 | |
| 	uint64 access;
 | |
| 	MTPinputPeer input;
 | |
| 	MTPinputUser inputUser;
 | |
| 
 | |
| 	int32 colorIndex;
 | |
| 	style::color color;
 | |
| 	ImagePtr photo;
 | |
| 
 | |
| 	int32 nameVersion;
 | |
| 
 | |
| 	NotifySettingsPtr notify;
 | |
| };
 | |
| 
 | |
| class PeerLink : public ITextLink {
 | |
| public:
 | |
| 	PeerLink(PeerData *peer) : _peer(peer) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| 	PeerData *peer() const {
 | |
| 		return _peer;
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	PeerData *_peer;
 | |
| };
 | |
| 
 | |
| struct PhotoData;
 | |
| struct UserData : public PeerData {
 | |
| 	UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1) {
 | |
| 	}
 | |
| 	void setPhoto(const MTPUserProfilePhoto &photo);
 | |
| 	void setName(const QString &first, const QString &last, const QString &phoneName, const QString &username);
 | |
| 	void setPhone(const QString &newPhone);
 | |
| 	void nameUpdated();
 | |
| 
 | |
| 	QString firstName;
 | |
| 	QString lastName;
 | |
| 	QString username;
 | |
| 	QString phone;
 | |
| 	Text nameText;
 | |
| 	PhotoId photoId;
 | |
| 	TextLinkPtr lnk;
 | |
| 	int32 onlineTill;
 | |
| 	int32 contact; // -1 - not contact, cant add (self, empty, deleted, foreign), 0 - not contact, can add (request), 1 - contact
 | |
| 
 | |
| 	typedef QList<PhotoData*> Photos;
 | |
| 	Photos photos;
 | |
| 	int32 photosCount; // -1 not loaded, 0 all loaded
 | |
| };
 | |
| 
 | |
| struct ChatData : public PeerData {
 | |
| 	ChatData(const PeerId &id) : PeerData(id), count(0), date(0), version(0), left(false), forbidden(true), photoId(0) {
 | |
| 	}
 | |
| 	void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = 0);
 | |
| 	int32 count;
 | |
| 	int32 date;
 | |
| 	int32 version;
 | |
| 	int32 admin;
 | |
| 	bool left;
 | |
| 	bool forbidden;
 | |
| 	typedef QMap<UserData*, int32> Participants;
 | |
| 	Participants participants;
 | |
| 	typedef QMap<UserData*, bool> CanKick;
 | |
| 	CanKick cankick;
 | |
| 	ImagePtr photoFull;
 | |
| 	PhotoId photoId;
 | |
| 	// geo
 | |
| };
 | |
| 
 | |
| typedef QMap<char, QPixmap> PreparedPhotoThumbs;
 | |
| struct PhotoData {
 | |
| 	PhotoData(const PhotoId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr()) :
 | |
| 		id(id), access(access), user(user), date(date), thumb(thumb), medium(medium), full(full), chat(0) {
 | |
| 	}
 | |
| 	void forget() {
 | |
| 		thumb->forget();
 | |
| 		medium->forget();
 | |
| 		full->forget();
 | |
| 	}
 | |
| 	PhotoId id;
 | |
| 	uint64 access;
 | |
| 	int32 user;
 | |
| 	int32 date;
 | |
| 	ImagePtr thumb;
 | |
| 	ImagePtr medium;
 | |
| 	ImagePtr full;
 | |
| 	ChatData *chat; // for chat photos connection
 | |
| 	// geo, caption
 | |
| 
 | |
| 	int32 cachew;
 | |
| 	QPixmap cache;
 | |
| };
 | |
| 
 | |
| class PhotoLink : public ITextLink {
 | |
| public:
 | |
| 	PhotoLink(PhotoData *photo) : _photo(photo), _peer(0) {
 | |
| 	}
 | |
| 	PhotoLink(PhotoData *photo, PeerData *peer) : _photo(photo), _peer(peer) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| 	PhotoData *photo() const {
 | |
| 		return _photo;
 | |
| 	}
 | |
| 	PeerData *peer() const {
 | |
| 		return _peer;
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	PhotoData *_photo;
 | |
| 	PeerData *_peer;
 | |
| };
 | |
| 
 | |
| enum FileStatus {
 | |
| 	FileFailed = -1,
 | |
| 	FileUploading = 0,
 | |
| 	FileReady = 1,
 | |
| };
 | |
| 
 | |
| struct VideoData {
 | |
| 	VideoData(const VideoId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | |
| 
 | |
| 	void forget() {
 | |
| 		thumb->forget();
 | |
| 	}
 | |
| 
 | |
| 	void save(const QString &toFile);
 | |
| 
 | |
| 	void cancel(bool beforeDownload = false) {
 | |
| 		mtpFileLoader *l = loader;
 | |
| 		loader = 0;
 | |
| 		if (l) {
 | |
| 			l->cancel();
 | |
| 			l->deleteLater();
 | |
| 			l->rpcInvalidate();
 | |
| 		}
 | |
| 		location = FileLocation();
 | |
| 		if (!beforeDownload) {
 | |
| 			openOnSave = openOnSaveMsgId = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void finish() {
 | |
| 		if (loader->done()) {
 | |
| 			location = FileLocation(loader->fileType(), loader->fileName());
 | |
| 		}
 | |
| 		loader->deleteLater();
 | |
| 		loader->rpcInvalidate();
 | |
| 		loader = 0;
 | |
| 	}
 | |
| 
 | |
| 	QString already(bool check = false);
 | |
| 
 | |
| 	VideoId id;
 | |
| 	uint64 access;
 | |
| 	int32 user;
 | |
| 	int32 date;
 | |
| 	int32 duration;
 | |
| 	int32 w, h;
 | |
| 	ImagePtr thumb;
 | |
| 	int32 dc, size;
 | |
| 	// geo, caption
 | |
| 
 | |
| 	FileStatus status;
 | |
| 	int32 uploadOffset;
 | |
| 
 | |
| 	mtpTypeId fileType;
 | |
| 	int32 openOnSave, openOnSaveMsgId;
 | |
| 	mtpFileLoader *loader;
 | |
| 	FileLocation location;
 | |
| };
 | |
| 
 | |
| class VideoLink : public ITextLink {
 | |
| public:
 | |
| 	VideoLink(VideoData *video) : _video(video) {
 | |
| 	}
 | |
| 	VideoData *video() const {
 | |
| 		return _video;
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	VideoData *_video;
 | |
| };
 | |
| 
 | |
| class VideoSaveLink : public VideoLink {
 | |
| public:
 | |
| 	VideoSaveLink(VideoData *video) : VideoLink(video) {
 | |
| 	}
 | |
| 	void doSave(bool forceSavingAs = false) const;
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| class VideoOpenLink : public VideoLink {
 | |
| public:
 | |
| 	VideoOpenLink(VideoData *video) : VideoLink(video) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| class VideoCancelLink : public VideoLink {
 | |
| public:
 | |
| 	VideoCancelLink(VideoData *video) : VideoLink(video) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| struct AudioData {
 | |
| 	AudioData(const AudioId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
 | |
| 
 | |
| 	void forget() {
 | |
| 	}
 | |
| 
 | |
| 	void save(const QString &toFile);
 | |
| 
 | |
| 	void cancel(bool beforeDownload = false) {
 | |
| 		mtpFileLoader *l = loader;
 | |
| 		loader = 0;
 | |
| 		if (l) {
 | |
| 			l->cancel();
 | |
| 			l->deleteLater();
 | |
| 			l->rpcInvalidate();
 | |
| 		}
 | |
| 		location = FileLocation();
 | |
| 		if (!beforeDownload) {
 | |
| 			openOnSave = openOnSaveMsgId = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void finish() {
 | |
| 		if (loader->done()) {
 | |
| 			location = FileLocation(loader->fileType(), loader->fileName());
 | |
| 			data = loader->bytes();
 | |
| 		}
 | |
| 		loader->deleteLater();
 | |
| 		loader->rpcInvalidate();
 | |
| 		loader = 0;
 | |
| 	}
 | |
| 
 | |
| 	QString already(bool check = false);
 | |
| 
 | |
| 	AudioId id;
 | |
| 	uint64 access;
 | |
| 	int32 user;
 | |
| 	int32 date;
 | |
| 	QString mime;
 | |
| 	int32 duration;
 | |
| 	int32 dc;
 | |
| 	int32 size;
 | |
| 
 | |
| 	FileStatus status;
 | |
| 	int32 uploadOffset;
 | |
| 
 | |
| 	int32 openOnSave, openOnSaveMsgId;
 | |
| 	mtpFileLoader *loader;
 | |
| 	FileLocation location;
 | |
| 	QByteArray data;
 | |
| 	int32 md5[8];
 | |
| };
 | |
| 
 | |
| class AudioLink : public ITextLink {
 | |
| public:
 | |
| 	AudioLink(AudioData *audio) : _audio(audio) {
 | |
| 	}
 | |
| 	AudioData *audio() const {
 | |
| 		return _audio;
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	AudioData *_audio;
 | |
| };
 | |
| 
 | |
| class AudioSaveLink : public AudioLink {
 | |
| public:
 | |
| 	AudioSaveLink(AudioData *audio) : AudioLink(audio) {
 | |
| 	}
 | |
| 	void doSave(bool forceSavingAs = false) const;
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| class AudioOpenLink : public AudioLink {
 | |
| public:
 | |
| 	AudioOpenLink(AudioData *audio) : AudioLink(audio) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| class AudioCancelLink : public AudioLink {
 | |
| public:
 | |
| 	AudioCancelLink(AudioData *audio) : AudioLink(audio) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| enum DocumentType {
 | |
| 	FileDocument,
 | |
| 	VideoDocument,
 | |
| 	AudioDocument,
 | |
| 	StickerDocument,
 | |
| 	AnimatedDocument
 | |
| };
 | |
| struct DocumentData {
 | |
| 	DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | |
| 	void setattributes(const QVector<MTPDocumentAttribute> &attributes);
 | |
| 
 | |
| 	void forget() {
 | |
| 		thumb->forget();
 | |
| 		sticker->forget();
 | |
| 	}
 | |
| 
 | |
| 	void save(const QString &toFile);
 | |
| 
 | |
| 	void cancel(bool beforeDownload = false) {
 | |
| 		mtpFileLoader *l = loader;
 | |
| 		loader = 0;
 | |
| 		if (l) {
 | |
| 			l->cancel();
 | |
| 			l->deleteLater();
 | |
| 			l->rpcInvalidate();
 | |
| 		}
 | |
| 		location = FileLocation();
 | |
| 		if (!beforeDownload) {
 | |
| 			openOnSave = openOnSaveMsgId = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void finish() {
 | |
| 		if (loader->done()) {
 | |
| 			location = FileLocation(loader->fileType(), loader->fileName());
 | |
| 			data = loader->bytes();
 | |
| 		}
 | |
| 		loader->deleteLater();
 | |
| 		loader->rpcInvalidate();
 | |
| 		loader = 0;
 | |
| 	}
 | |
| 
 | |
| 	QString already(bool check = false);
 | |
| 
 | |
| 	DocumentId id;
 | |
| 	DocumentType type;
 | |
| 	QSize dimensions;
 | |
| 	int32 duration;
 | |
| 	uint64 access;
 | |
| 	int32 date;
 | |
| 	QString name, mime;
 | |
| 	ImagePtr thumb;
 | |
| 	int32 dc;
 | |
| 	int32 size;
 | |
| 
 | |
| 	FileStatus status;
 | |
| 	int32 uploadOffset;
 | |
| 
 | |
| 	int32 openOnSave, openOnSaveMsgId;
 | |
| 	mtpFileLoader *loader;
 | |
| 	FileLocation location;
 | |
| 
 | |
| 	QByteArray data;
 | |
| 	ImagePtr sticker;
 | |
| 
 | |
| 	int32 md5[8];
 | |
| };
 | |
| 
 | |
| class DocumentLink : public ITextLink {
 | |
| public:
 | |
| 	DocumentLink(DocumentData *document) : _document(document) {
 | |
| 	}
 | |
| 	DocumentData *document() const {
 | |
| 		return _document;
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	DocumentData *_document;
 | |
| };
 | |
| 
 | |
| class DocumentSaveLink : public DocumentLink {
 | |
| public:
 | |
| 	DocumentSaveLink(DocumentData *document) : DocumentLink(document) {
 | |
| 	}
 | |
| 	void doSave(bool forceSavingAs = false) const;
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| class DocumentOpenLink : public DocumentLink {
 | |
| public:
 | |
| 	DocumentOpenLink(DocumentData *document) : DocumentLink(document) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| class DocumentCancelLink : public DocumentLink {
 | |
| public:
 | |
| 	DocumentCancelLink(DocumentData *document) : DocumentLink(document) {
 | |
| 	}
 | |
| 	void onClick(Qt::MouseButton button) const;
 | |
| };
 | |
| 
 | |
| MsgId clientMsgId();
 | |
| 
 | |
| struct History;
 | |
| struct Histories : public QHash<PeerId, History*>, public Animated {
 | |
| 	typedef QHash<PeerId, History*> Parent;
 | |
| 
 | |
| 	Histories() : unreadFull(0), unreadMuted(0) {
 | |
| 	}
 | |
| 
 | |
| 	void regTyping(History *history, UserData *user);
 | |
| 	bool animStep(float64 ms);
 | |
| 
 | |
| 	void clear();
 | |
| 	Parent::iterator erase(Parent::iterator i);
 | |
| 	void remove(const PeerId &peer);
 | |
| 	~Histories() {
 | |
| 		clear();
 | |
| 
 | |
| 		unreadFull = unreadMuted = 0;
 | |
| 	}
 | |
| 
 | |
| 	HistoryItem *addToBack(const MTPmessage &msg, int msgState = 1); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message
 | |
| //	HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true);
 | |
| 
 | |
| 	typedef QMap<History*, uint64> TypingHistories; // when typing in this history started
 | |
| 	TypingHistories typing;
 | |
| 
 | |
| 	int32 unreadFull, unreadMuted;
 | |
| };
 | |
| 
 | |
| struct HistoryBlock;
 | |
| 
 | |
| struct DialogRow {
 | |
| 	DialogRow(History *history = 0, DialogRow *prev = 0, DialogRow *next = 0, int32 pos = 0) : prev(prev), next(next), history(history), pos(pos), attached(0) {
 | |
| 	}
 | |
| 
 | |
| 	void paint(QPainter &p, int32 w, bool act, bool sel) const;
 | |
| 
 | |
| 	DialogRow *prev, *next;
 | |
| 	History *history;
 | |
| 	int32 pos;
 | |
| 	void *attached; // for any attached data, for example View in contacts list
 | |
| };
 | |
| 
 | |
| struct FakeDialogRow {
 | |
| 	FakeDialogRow(HistoryItem *item) : _item(item), _cacheFor(0), _cache(st::dlgRichMinWidth) {
 | |
| 	}
 | |
| 
 | |
| 	void paint(QPainter &p, int32 w, bool act, bool sel) const;
 | |
| 
 | |
| 	HistoryItem *_item;
 | |
| 	mutable const HistoryItem *_cacheFor;
 | |
| 	mutable Text _cache;
 | |
| };
 | |
| 
 | |
| enum HistoryMediaType {
 | |
| 	MediaTypePhoto,
 | |
| 	MediaTypeVideo,
 | |
| 	MediaTypeGeo,
 | |
| 	MediaTypeContact,
 | |
| 	MediaTypeAudio,
 | |
| 	MediaTypeDocument,
 | |
| 	MediaTypeSticker,
 | |
| 	MediaTypeImageLink,
 | |
| 
 | |
| 	MediaTypeCount
 | |
| };
 | |
| 
 | |
| enum MediaOverviewType {
 | |
| 	OverviewPhotos,
 | |
| 	OverviewVideos,
 | |
| 	OverviewDocuments,
 | |
| 	OverviewAudios,
 | |
| 
 | |
| 	OverviewCount
 | |
| };
 | |
| 
 | |
| inline MediaOverviewType mediaToOverviewType(HistoryMediaType t) {
 | |
| 	switch (t) {
 | |
| 	case MediaTypePhoto: return OverviewPhotos;
 | |
| 	case MediaTypeVideo: return OverviewVideos;
 | |
| 	case MediaTypeDocument: return OverviewDocuments;
 | |
| //	case MediaTypeSticker: return OverviewDocuments;
 | |
| 	case MediaTypeAudio: return OverviewAudios;
 | |
| 	}
 | |
| 	return OverviewCount;
 | |
| }
 | |
| 
 | |
| inline MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) {
 | |
| 	switch (type) {
 | |
| 	case OverviewPhotos: return MTP_inputMessagesFilterPhotos();
 | |
| 	case OverviewVideos: return MTP_inputMessagesFilterVideo();
 | |
| 	case OverviewDocuments: return MTP_inputMessagesFilterDocument();
 | |
| 	case OverviewAudios: return MTP_inputMessagesFilterAudio();
 | |
| 	default: type = OverviewCount; break;
 | |
| 	}
 | |
| 	return MTPMessagesFilter();
 | |
| }
 | |
| 
 | |
| struct MessageCursor {
 | |
| 	MessageCursor() : position(0), anchor(0), scroll(QFIXED_MAX) {
 | |
| 	}
 | |
| 	MessageCursor(int position, int anchor, int scroll) : position(position), anchor(anchor), scroll(scroll) {
 | |
| 	}
 | |
| 	MessageCursor(const QTextEdit &edit) {
 | |
| 		fillFrom(edit);
 | |
| 	}
 | |
| 	void fillFrom(const QTextEdit &edit) {
 | |
| 		QTextCursor c = edit.textCursor();
 | |
| 		position = c.position();
 | |
| 		anchor = c.anchor();
 | |
| 		QScrollBar *s = edit.verticalScrollBar();
 | |
| 		scroll = s ? s->value() : QFIXED_MAX;
 | |
| 	}
 | |
| 	void applyTo(QTextEdit &edit, bool *lock = 0) {
 | |
| 		if (lock) *lock = true;
 | |
| 		QTextCursor c = edit.textCursor();
 | |
| 		c.setPosition(anchor, QTextCursor::MoveAnchor);
 | |
| 		c.setPosition(position, QTextCursor::KeepAnchor);
 | |
| 		edit.setTextCursor(c);
 | |
| 		QScrollBar *s = edit.verticalScrollBar();
 | |
| 		if (s) s->setValue(scroll);
 | |
| 		if (lock) *lock = false;
 | |
| 	}
 | |
| 	int position, anchor, scroll;
 | |
| };
 | |
| 
 | |
| class HistoryMedia;
 | |
| class HistoryMessage;
 | |
| class HistoryUnreadBar;
 | |
| struct History : public QList<HistoryBlock*> {
 | |
| 	History(const PeerId &peerId);
 | |
| 
 | |
| 	typedef QList<HistoryBlock*> Parent;
 | |
| 	void clear(bool leaveItems = false);
 | |
| 	Parent::iterator erase(Parent::iterator i);
 | |
| 	void blockResized(HistoryBlock *block, int32 dh);
 | |
| 	void removeBlock(HistoryBlock *block);
 | |
| 
 | |
| 	~History() {
 | |
| 		clear();
 | |
| 	}
 | |
| 
 | |
| 	HistoryItem *createItem(HistoryBlock *block, const MTPmessage &msg, bool newMsg, bool returnExisting = false);
 | |
| 	HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, HistoryMessage *msg);
 | |
| 	HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc);
 | |
| 
 | |
| 	HistoryItem *addToBackService(MsgId msgId, QDateTime date, const QString &text, bool out = false, bool unread = false, HistoryMedia *media = 0, bool newMsg = true);
 | |
| 	HistoryItem *addToBack(const MTPmessage &msg, bool newMsg = true);
 | |
| 	HistoryItem *addToHistory(const MTPmessage &msg);
 | |
| 	HistoryItem *addToBackForwarded(MsgId id, HistoryMessage *item);
 | |
| 	HistoryItem *addToBackDocument(MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc);
 | |
| 
 | |
| 	void addToFront(const QVector<MTPMessage> &slice);
 | |
| 	void addToBack(const QVector<MTPMessage> &slice);
 | |
| 	void createInitialDateBlock(const QDateTime &date);
 | |
| 	HistoryItem *doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg);
 | |
| 
 | |
| 	void newItemAdded(HistoryItem *item);
 | |
| 	void unregTyping(UserData *from);
 | |
| 
 | |
| 	void inboxRead(HistoryItem *wasRead);
 | |
| 	void outboxRead(HistoryItem *wasRead);
 | |
| 
 | |
| 	void setUnreadCount(int32 newUnreadCount, bool psUpdate = true);
 | |
| 	void setMsgCount(int32 newMsgCount);
 | |
| 	void setMute(bool newMute);
 | |
| 	void getNextShowFrom(HistoryBlock *block, int32 i);
 | |
| 	void addUnreadBar();
 | |
| 	void clearNotifications();
 | |
| 
 | |
| 	bool readyForWork() const; // all unread loaded or loaded around activeMsgId
 | |
| 	bool loadedAtBottom() const; // last message is in the list
 | |
| 	bool loadedAtTop() const; // nothing was added after loading history back
 | |
| 
 | |
| 	void fixLastMessage(bool wasAtBottom);
 | |
| 
 | |
| 	void loadAround(MsgId msgId);
 | |
| 	bool canShowAround(MsgId msgId) const;
 | |
| 
 | |
| 	MsgId minMsgId() const;
 | |
| 	MsgId maxMsgId() const;
 | |
| 
 | |
| 	int32 geomResize(int32 newWidth, int32 *ytransform = 0, bool dontRecountText = false); // return new size
 | |
| 	int32 width, height, msgCount, unreadCount;
 | |
| 	int32 inboxReadTill, outboxReadTill;
 | |
| 	HistoryItem *showFrom;
 | |
| 	HistoryUnreadBar *unreadBar;
 | |
| 
 | |
| 	PeerData *peer;
 | |
| 	bool oldLoaded, newLoaded;
 | |
| 	HistoryItem *last;
 | |
| 	MsgId activeMsgId;
 | |
| 
 | |
| 	typedef QList<HistoryItem*> NotifyQueue;
 | |
| 	NotifyQueue notifies;
 | |
| 
 | |
| 	void removeNotification(HistoryItem *item) {
 | |
| 		if (!notifies.isEmpty()) {
 | |
| 			for (NotifyQueue::iterator i = notifies.begin(), e = notifies.end(); i != e; ++i) {
 | |
| 				if ((*i) == item) {
 | |
| 					notifies.erase(i);
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	HistoryItem *currentNotification() {
 | |
| 		return notifies.isEmpty() ? 0 : notifies.front();
 | |
| 	}
 | |
| 	void skipNotification() {
 | |
| 		if (!notifies.isEmpty()) {
 | |
| 			notifies.pop_front();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void itemReplaced(HistoryItem *old, HistoryItem *item) {
 | |
| 		if (!notifies.isEmpty()) {
 | |
| 			for (NotifyQueue::iterator i = notifies.begin(), e = notifies.end(); i != e; ++i) {
 | |
| 				if ((*i) == old) {
 | |
| 					*i = item;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if (last == old) {
 | |
| 			last = item;
 | |
| 		}
 | |
| 		// showFrom can't be detached
 | |
| 	}
 | |
| 
 | |
| 	QString draft;
 | |
| 	MessageCursor draftCursor;
 | |
| 	int32 lastWidth, lastScrollTop;
 | |
| 	bool mute;
 | |
| 
 | |
| 	mtpRequestId sendRequestId;
 | |
| 
 | |
| 	// for dialog drawing
 | |
| 	Text nameText;
 | |
| 	void updateNameText();
 | |
| 
 | |
| 	mutable const HistoryItem *textCachedFor; // cache
 | |
| 	mutable Text lastItemTextCache;
 | |
| 
 | |
| 	void paintDialog(QPainter &p, int32 w, bool sel) const;
 | |
| 
 | |
| 	typedef QMap<QChar, DialogRow*> DialogLinks;
 | |
| 	DialogLinks dialogs;
 | |
| 	int32 posInDialogs;
 | |
| 
 | |
| 	typedef QMap<UserData*, uint64> TypingUsers;
 | |
| 	TypingUsers typing;
 | |
| 	QString typingStr;
 | |
| 	Text typingText;
 | |
| 	uint32 typingFrame;
 | |
| 	bool updateTyping(uint64 ms = 0, uint32 dots = 0, bool force = false);
 | |
| 	uint64 myTyping;
 | |
| 
 | |
| 	typedef QList<MsgId> MediaOverview;
 | |
| 	typedef QMap<MsgId, NullType> MediaOverviewIds;
 | |
| 
 | |
| 	MediaOverview _overview[OverviewCount];
 | |
| 	MediaOverviewIds _overviewIds[OverviewCount];
 | |
| 	int32 _overviewCount[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded
 | |
| 
 | |
| 	static const int32 ScrollMax = INT_MAX;
 | |
| };
 | |
| 
 | |
| struct DialogsList {
 | |
| 	DialogsList(bool sortByName) : begin(&last), end(&last), byName(sortByName), count(0), current(&last) {
 | |
| 	}
 | |
| 
 | |
| 	void adjustCurrent(int32 y, int32 h) const {
 | |
| 		int32 pos = (y > 0) ? (y / h) : 0;
 | |
| 		while (current->pos > pos && current != begin) {
 | |
| 			current = current->prev;
 | |
| 		}
 | |
| 		while (current->pos + 1 <= pos && current->next != end) {
 | |
| 			current = current->next;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void paint(QPainter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel) const {
 | |
| 		adjustCurrent(hFrom, st::dlgHeight);
 | |
| 
 | |
| 		DialogRow *drawFrom = current;
 | |
| 		p.translate(0, drawFrom->pos * st::dlgHeight);
 | |
| 		while (drawFrom != end && drawFrom->pos * st::dlgHeight < hTo) {
 | |
| 			drawFrom->paint(p, w, (drawFrom->history->peer == act), (drawFrom->history->peer == sel));
 | |
| 			drawFrom = drawFrom->next;
 | |
| 			p.translate(0, st::dlgHeight);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DialogRow *rowAtY(int32 y, int32 h) const {
 | |
| 		if (!count) return 0;
 | |
| 
 | |
| 		int32 pos = (y > 0) ? (y / h) : 0;
 | |
| 		adjustCurrent(y, h);
 | |
| 		return (pos == current->pos) ? current : 0;
 | |
| 	}
 | |
| 
 | |
| 	DialogRow *addToEnd(History *history, bool updatePos = true) {
 | |
| 		DialogRow *result = new DialogRow(history, end->prev, end, end->pos);
 | |
| 		end->pos++;
 | |
| 		if (begin == end) {
 | |
| 			begin = current = result;
 | |
| 			if (!byName && updatePos) history->posInDialogs = 0;
 | |
| 		} else {
 | |
| 			end->prev->next = result;
 | |
| 			if (!byName && updatePos) history->posInDialogs = end->prev->history->posInDialogs + 1;
 | |
| 		}
 | |
| 		rowByPeer.insert(history->peer->id, result);
 | |
| 		++count;
 | |
| 		return (end->prev = result);
 | |
| 	}
 | |
| 
 | |
| 	void bringToTop(DialogRow *row, bool updatePos = true) {
 | |
| 		if (!byName && updatePos && row != begin) {
 | |
| 			row->history->posInDialogs = begin->history->posInDialogs - 1;
 | |
| 		}
 | |
| 		insertBefore(row, begin);
 | |
| 	}
 | |
| 
 | |
| 	bool insertBefore(DialogRow *row, DialogRow *before) {
 | |
| 		if (row == before) return false;
 | |
| 
 | |
| 		if (current == row) current = row->prev;
 | |
| 
 | |
| 		DialogRow *updateTill = row->prev;
 | |
| 		remove(row);
 | |
| 
 | |
| 		// insert row
 | |
| 		row->next = before; // update row
 | |
| 		row->prev = before->prev;
 | |
| 		row->next->prev = row; // update row->next
 | |
| 		if (row->prev) { // update row->prev
 | |
| 			row->prev->next = row;
 | |
| 		} else {
 | |
| 			begin = row;
 | |
| 		}
 | |
| 
 | |
| 		// update y
 | |
| 		for (DialogRow *n = row; n != updateTill; n = n->next) {
 | |
| 			n->next->pos++;
 | |
| 			row->pos--;
 | |
| 		}
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	bool insertAfter(DialogRow *row, DialogRow *after) {
 | |
| 		if (row == after) return false;
 | |
| 
 | |
| 		if (current == row) current = row->next;
 | |
| 
 | |
| 		DialogRow *updateFrom = row->next;
 | |
| 		remove(row);
 | |
| 
 | |
| 		// insert row
 | |
| 		row->prev = after; // update row
 | |
| 		row->next = after->next;
 | |
| 		row->prev->next = row; // update row->prev
 | |
| 		row->next->prev = row; // update row->next
 | |
| 
 | |
| 		// update y
 | |
| 		for (DialogRow *n = updateFrom; n != row; n = n->next) {
 | |
| 			n->pos--;
 | |
| 			row->pos++;
 | |
| 		}
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	DialogRow *adjustByName(const PeerData *peer) {
 | |
| 		if (!byName) return 0;
 | |
| 
 | |
| 		RowByPeer::iterator i = rowByPeer.find(peer->id);
 | |
| 		if (i == rowByPeer.cend()) return 0;
 | |
| 
 | |
| 		DialogRow *row = i.value(), *change = row;
 | |
| 		while (change->prev && change->prev->history->peer->name > peer->name) {
 | |
| 			change = change->prev;
 | |
| 		}
 | |
| 		if (!insertBefore(row, change)) {
 | |
| 			while (change->next != end && change->next->history->peer->name < peer->name) {
 | |
| 				change = change->next;
 | |
| 			}
 | |
| 			insertAfter(row, change);
 | |
| 		}
 | |
| 		return row;
 | |
| 	}
 | |
| 
 | |
| 	DialogRow *addByName(History *history) {
 | |
| 		if (!byName) return 0;
 | |
| 
 | |
| 		DialogRow *row = addToEnd(history), *change = row;
 | |
| 		const QString &peerName(history->peer->name);
 | |
| 		while (change->prev && change->prev->history->peer->name > peerName) {
 | |
| 			change = change->prev;
 | |
| 		}
 | |
| 		if (!insertBefore(row, change)) {
 | |
| 			while (change->next != end && change->next->history->peer->name < peerName) {
 | |
| 				change = change->next;
 | |
| 			}
 | |
| 			insertAfter(row, change);
 | |
| 		}
 | |
| 		return row;
 | |
| 	}
 | |
| 
 | |
| 	void adjustByPos(DialogRow *row) {
 | |
| 		if (byName) return;
 | |
| 
 | |
| 		DialogRow *change = row;
 | |
| 		while (change->prev && change->prev->history->posInDialogs > row->history->posInDialogs) {
 | |
| 			change = change->prev;
 | |
| 		}
 | |
| 		if (!insertBefore(row, change)) {
 | |
| 			while (change->next != end && change->next->history->posInDialogs < row->history->posInDialogs) {
 | |
| 				change = change->next;
 | |
| 			}
 | |
| 			insertAfter(row, change);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DialogRow *addByPos(History *history) {
 | |
| 		if (byName) return 0;
 | |
| 
 | |
| 		DialogRow *row = addToEnd(history, false);
 | |
| 		adjustByPos(row);
 | |
| 		return row;
 | |
| 	}
 | |
| 
 | |
| 	bool del(const PeerId &peerId, DialogRow *replacedBy = 0);
 | |
| 
 | |
| 	void remove(DialogRow *row) {
 | |
| 		row->next->prev = row->prev; // update row->next
 | |
| 		if (row->prev) { // update row->prev
 | |
| 			row->prev->next = row->next;
 | |
| 		} else {
 | |
| 			begin = row->next;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void clear() {
 | |
| 		while (begin != end) {
 | |
| 			current = begin;
 | |
| 			begin = begin->next;
 | |
| 			delete current;
 | |
| 		}
 | |
| 		current = begin;
 | |
| 		rowByPeer.clear();
 | |
| 		count = 0;
 | |
| 	}
 | |
| 
 | |
| 	~DialogsList() {
 | |
| 		clear();
 | |
| 	}
 | |
| 
 | |
| 	DialogRow last;
 | |
| 	DialogRow *begin, *end;
 | |
| 	bool byName;
 | |
| 	int32 count;
 | |
| 
 | |
| 	typedef QHash<PeerId, DialogRow*> RowByPeer;
 | |
| 	RowByPeer rowByPeer;
 | |
| 
 | |
| 	mutable DialogRow *current; // cache
 | |
| };
 | |
| 
 | |
| struct DialogsIndexed {
 | |
| 	DialogsIndexed(bool sortByName) : byName(sortByName), list(byName) {
 | |
| 	}
 | |
| 
 | |
| 	History::DialogLinks addToEnd(History *history) {
 | |
| 		History::DialogLinks result;
 | |
| 		DialogsList::RowByPeer::const_iterator i = list.rowByPeer.find(history->peer->id);
 | |
| 		if (i != list.rowByPeer.cend()) {
 | |
| 			return i.value()->history->dialogs;
 | |
| 		}
 | |
| 
 | |
| 		result.insert(0, list.addToEnd(history));
 | |
| 		for (PeerData::NameFirstChars::const_iterator i = history->peer->chars.cbegin(), e = history->peer->chars.cend(); i != e; ++i) {
 | |
| 			DialogsIndex::iterator j = index.find(*i);
 | |
| 			if (j == index.cend()) {
 | |
| 				j = index.insert(*i, new DialogsList(byName));
 | |
| 			}
 | |
| 			result.insert(*i, j.value()->addToEnd(history));
 | |
| 		}
 | |
| 
 | |
| 		return result;
 | |
| 	}
 | |
| 
 | |
| 	DialogRow *addByName(History *history) {
 | |
| 		DialogsList::RowByPeer::const_iterator i = list.rowByPeer.constFind(history->peer->id);
 | |
| 		if (i != list.rowByPeer.cend()) {
 | |
| 			return i.value();
 | |
| 		}
 | |
| 
 | |
| 		DialogRow *res = list.addByName(history);
 | |
| 		for (PeerData::NameFirstChars::const_iterator i = history->peer->chars.cbegin(), e = history->peer->chars.cend(); i != e; ++i) {
 | |
| 			DialogsIndex::iterator j = index.find(*i);
 | |
| 			if (j == index.cend()) {
 | |
| 				j = index.insert(*i, new DialogsList(byName));
 | |
| 			}
 | |
| 			j.value()->addByName(history);
 | |
| 		}
 | |
| 		return res;
 | |
| 	}
 | |
| 
 | |
| 	void bringToTop(const History::DialogLinks &links) {
 | |
| 		for (History::DialogLinks::const_iterator i = links.cbegin(), e = links.cend(); i != e; ++i) {
 | |
| 			if (i.key() == QChar(0)) {
 | |
| 				list.bringToTop(i.value());
 | |
| 			} else {
 | |
| 				DialogsIndex::iterator j = index.find(i.key());
 | |
| 				if (j != index.cend()) {
 | |
| 					j.value()->bringToTop(i.value());
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
 | |
| 
 | |
| 	void del(const PeerData *peer, DialogRow *replacedBy = 0) {
 | |
| 		if (list.del(peer->id, replacedBy)) {
 | |
| 			for (PeerData::NameFirstChars::const_iterator i = peer->chars.cbegin(), e = peer->chars.cend(); i != e; ++i) {
 | |
| 				DialogsIndex::iterator j = index.find(*i);
 | |
| 				if (j != index.cend()) {
 | |
| 					j.value()->del(peer->id, replacedBy);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	~DialogsIndexed() {
 | |
| 		clear();
 | |
| 	}
 | |
| 
 | |
| 	void clear();
 | |
| 
 | |
| 	bool byName;
 | |
| 	DialogsList list;
 | |
| 	typedef QMap<QChar, DialogsList*> DialogsIndex;
 | |
| 	DialogsIndex index;
 | |
| };
 | |
| 
 | |
| struct HistoryBlock : public QVector<HistoryItem*> {
 | |
| 	HistoryBlock(History *hist) : y(0), height(0), history(hist) {
 | |
| 	}
 | |
| 
 | |
| 	typedef QVector<HistoryItem*> Parent;
 | |
| 	void clear(bool leaveItems = false);
 | |
| 	Parent::iterator erase(Parent::iterator i);
 | |
| 	~HistoryBlock() {
 | |
| 		clear();
 | |
| 	}
 | |
| 	void removeItem(HistoryItem *item);
 | |
| 
 | |
| 	int32 geomResize(int32 newWidth, int32 *ytransform, bool dontRecountText); // return new size
 | |
| 	int32 y, height;
 | |
| 	History *history;
 | |
| };
 | |
| 
 | |
| class HistoryElem {
 | |
| public:
 | |
| 
 | |
| 	HistoryElem() : _height(0), _maxw(0) {
 | |
| 	}
 | |
| 
 | |
| 	virtual void initDimensions(const HistoryItem *parent = 0) = 0;
 | |
| 	virtual int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0) = 0; // return new height
 | |
| 
 | |
| 	int32 height() const {
 | |
| 		return _height;
 | |
| 	}
 | |
| 	int32 maxWidth() const {
 | |
| 		return _maxw;
 | |
| 	}
 | |
| 
 | |
| 	virtual ~HistoryElem() {
 | |
| 	}
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	mutable int32 _height, _maxw, _minh;
 | |
| 
 | |
| };
 | |
| 
 | |
| class ItemAnimations : public Animated {
 | |
| public:
 | |
| 
 | |
| 	bool animStep(float64 ms);
 | |
| 	uint64 animate(const HistoryItem *item, uint64 ms);
 | |
| 	void remove(const HistoryItem *item);
 | |
| 
 | |
| private:
 | |
| 	typedef QMap<const HistoryItem*, uint64> Animations;
 | |
| 	Animations _animations;
 | |
| };
 | |
| 
 | |
| ItemAnimations &itemAnimations();
 | |
| 
 | |
| class HistoryMedia;
 | |
| class HistoryItem : public HistoryElem {
 | |
| public:
 | |
| 
 | |
| 	HistoryItem(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime msgDate, int32 from);
 | |
| 
 | |
| 	enum {
 | |
| 		MsgType = 0,
 | |
| 		DateType,
 | |
| 		UnreadBarType
 | |
| 	};
 | |
| 
 | |
| 	virtual void draw(QPainter &p, uint32 selection) const = 0;
 | |
| 	History *history() {
 | |
| 		return _history;
 | |
| 	}
 | |
| 	const History *history() const {
 | |
| 		return _history;
 | |
| 	}
 | |
| 	UserData *from() {
 | |
| 		return _from;
 | |
| 	}
 | |
| 	const UserData *from() const {
 | |
| 		return _from;
 | |
| 	}
 | |
| 	HistoryBlock *block() {
 | |
| 		return _block;
 | |
| 	}
 | |
| 	const HistoryBlock *block() const {
 | |
| 		return _block;
 | |
| 	}
 | |
| 	void destroy();
 | |
| 	void detach();
 | |
| 	void detachFast();
 | |
| 	bool detached() const {
 | |
| 		return !_block;
 | |
| 	}
 | |
| 	bool out() const {
 | |
| 		return _out;
 | |
| 	}
 | |
| 	bool unread() const {
 | |
| 		if ((_out && (id > 0 && id < _history->outboxReadTill)) || (!_out && id > 0 && id < _history->inboxReadTill)) return false;
 | |
| 		return _unread;
 | |
| 	}
 | |
| 	virtual bool needCheck() const {
 | |
| 		return true;
 | |
| 	}
 | |
| 	virtual bool hasPoint(int32 x, int32 y) const {
 | |
| 		return false;
 | |
| 	}
 | |
| 	virtual void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const {
 | |
| 		lnk = TextLinkPtr();
 | |
| 		inText = false;
 | |
| 	}
 | |
| 	virtual void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { // from text
 | |
| 		upon = hasPoint(x, y);
 | |
| 		symbol = upon ? 0xFFFF : 0;
 | |
| 		after = false;
 | |
| 	}
 | |
| 	virtual uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
 | |
| 		return (from << 16) | to;
 | |
| 	}
 | |
| 	virtual int32 itemType() const {
 | |
| 		return MsgType;
 | |
| 	}
 | |
| 	virtual bool serviceMsg() const {
 | |
| 		return false;
 | |
| 	}
 | |
| 	virtual void updateMedia(const MTPMessageMedia &media) {
 | |
| 	}
 | |
| 	virtual void updateStickerEmoji() {
 | |
| 	}
 | |
| 
 | |
| 	virtual QString selectedText(uint32 selection) const {
 | |
| 		return qsl("[-]");
 | |
| 	}
 | |
| 
 | |
| 	virtual void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const = 0;
 | |
|     virtual QString notificationHeader() const {
 | |
|         return QString();
 | |
|     }
 | |
|     virtual QString notificationText() const = 0;
 | |
| 	void markRead();
 | |
| 
 | |
| 	int32 y, id;
 | |
| 	QDateTime date;
 | |
| 
 | |
| 	virtual HistoryMedia *getMedia(bool inOverview = false) const {
 | |
| 		return 0;
 | |
| 	}
 | |
| 	virtual QString time() const {
 | |
| 		return QString();
 | |
| 	}
 | |
| 	virtual int32 timeWidth() const {
 | |
| 		return 0;
 | |
| 	}
 | |
| 	virtual bool animating() const {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	virtual ~HistoryItem();
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	UserData *_from;
 | |
| 	mutable int32 _fromVersion;
 | |
| 	History *_history;
 | |
| 	HistoryBlock *_block;
 | |
| 	bool _out, _unread;
 | |
| 
 | |
| };
 | |
| 
 | |
| HistoryItem *regItem(HistoryItem *item, bool returnExisting = false);
 | |
| 
 | |
| class HistoryMedia : public HistoryElem {
 | |
| public:
 | |
| 
 | |
| 	HistoryMedia(int32 width = 0) : w(width) {
 | |
| 	}
 | |
| 
 | |
| 	virtual HistoryMediaType type() const = 0;
 | |
| 	virtual const QString inDialogsText() const = 0;
 | |
| 	virtual const QString inHistoryText() const = 0;
 | |
| 	virtual bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
 | |
| 	virtual int32 countHeight(const HistoryItem *parent, int32 width = -1) const {
 | |
| 		return height();
 | |
| 	}
 | |
| 	virtual int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0) {
 | |
| 		w = qMin(width, _maxw);
 | |
| 		return _height;
 | |
| 	}
 | |
| 	virtual TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
 | |
| 	virtual void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const = 0;
 | |
| 	virtual bool uploading() const {
 | |
| 		return false;
 | |
| 	}
 | |
| 	virtual HistoryMedia *clone() const = 0;
 | |
| 
 | |
| 	virtual void regItem(HistoryItem *item) {
 | |
| 	}
 | |
| 
 | |
| 	virtual void unregItem(HistoryItem *item) {
 | |
| 	}
 | |
| 
 | |
| 	virtual void updateFrom(const MTPMessageMedia &media) {
 | |
| 	}
 | |
| 
 | |
| 	virtual bool updateStickerEmoji() {
 | |
| 		return false;
 | |
| 	}
 | |
| 	
 | |
| 	virtual bool animating() const {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	int32 currentWidth() const {
 | |
| 		return w;
 | |
| 	}
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	int32 w;
 | |
| 
 | |
| };
 | |
| 
 | |
| class HistoryPhoto : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistoryPhoto(const MTPDphoto &photo, int32 width = 0);
 | |
| 	HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
 | |
| 
 | |
| 	void init();
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypePhoto;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| 	PhotoData *photo() const {
 | |
| 		return data;
 | |
| 	}
 | |
| 
 | |
| 	void updateFrom(const MTPMessageMedia &media);
 | |
| 
 | |
| 	TextLinkPtr lnk() const {
 | |
| 		return openl;
 | |
| 	}
 | |
| 
 | |
| 	virtual bool animating() const {
 | |
| 		if (data->full->loaded()) return false;
 | |
| 		return data->full->loading() ? true : !data->medium->loaded();
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	int16 pixw, pixh;
 | |
| 	PhotoData *data;
 | |
| 	TextLinkPtr openl;
 | |
| 
 | |
| };
 | |
| 
 | |
| QString formatSizeText(qint64 size);
 | |
| 
 | |
| class HistoryVideo : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistoryVideo(const MTPDvideo &video, int32 width = 0);
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypeVideo;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	bool uploading() const {
 | |
| 		return (data->status == FileUploading);
 | |
| 	}
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| 	void regItem(HistoryItem *item);
 | |
| 	void unregItem(HistoryItem *item);
 | |
| 
 | |
| private:
 | |
| 	VideoData *data;
 | |
| 	TextLinkPtr _openl, _savel, _cancell;
 | |
| 	
 | |
| 	QString _size;
 | |
| 	int32 _thumbw, _thumbx, _thumby;
 | |
| 
 | |
| 	mutable QString _dldTextCache, _uplTextCache;
 | |
| 	mutable int32 _dldDone, _uplDone;
 | |
| };
 | |
| 
 | |
| class HistoryAudio : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistoryAudio(const MTPDaudio &audio, int32 width = 0);
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypeAudio;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	bool uploading() const {
 | |
| 		return (data->status == FileUploading);
 | |
| 	}
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| 	void regItem(HistoryItem *item);
 | |
| 	void unregItem(HistoryItem *item);
 | |
| 
 | |
| private:
 | |
| 	AudioData *data;
 | |
| 	TextLinkPtr _openl, _savel, _cancell;
 | |
| 
 | |
| 	QString _size;
 | |
| 
 | |
| 	mutable QString _dldTextCache, _uplTextCache;
 | |
| 	mutable int32 _dldDone, _uplDone;
 | |
| };
 | |
| 
 | |
| class HistoryDocument : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistoryDocument(DocumentData *document, int32 width = 0);
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypeDocument;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	int32 countHeight(const HistoryItem *parent, int32 width = -1) const;
 | |
| 	bool uploading() const {
 | |
| 		return (data->status == FileUploading);
 | |
| 	}
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| 	DocumentData *document() {
 | |
| 		return data;
 | |
| 	}
 | |
| 
 | |
| 	void regItem(HistoryItem *item);
 | |
| 	void unregItem(HistoryItem *item);
 | |
| 
 | |
| 	void updateFrom(const MTPMessageMedia &media);
 | |
| 
 | |
| private:
 | |
| 
 | |
| 	DocumentData *data;
 | |
| 	TextLinkPtr _openl, _savel, _cancell;
 | |
| 
 | |
| 	int32 _namew;
 | |
| 	QString _name, _size;
 | |
| 	int32 _thumbw, _thumbx, _thumby;
 | |
| 
 | |
| 	mutable QString _dldTextCache, _uplTextCache;
 | |
| 	mutable int32 _dldDone, _uplDone;
 | |
| };
 | |
| 
 | |
| class HistorySticker : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistorySticker(DocumentData *document, int32 width = 0);
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypeSticker;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	int32 countHeight(const HistoryItem *parent, int32 width = -1) const;
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| 	DocumentData *document() {
 | |
| 		return data;
 | |
| 	}
 | |
| 
 | |
| 	void regItem(HistoryItem *item);
 | |
| 	void unregItem(HistoryItem *item);
 | |
| 
 | |
| 	void updateFrom(const MTPMessageMedia &media);
 | |
| 	bool updateStickerEmoji();
 | |
| 
 | |
| private:
 | |
| 
 | |
| 	int16 pixw, pixh;
 | |
| 	DocumentData *data;
 | |
| 	QString _emoji;
 | |
| 
 | |
| };
 | |
| 
 | |
| class HistoryContact : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistoryContact(int32 userId, const QString &first, const QString &last, const QString &phone);
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypeContact;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| 	void updateFrom(const MTPMessageMedia &media);
 | |
| 
 | |
| private:
 | |
| 	int32 userId;
 | |
| 	int32 phonew;
 | |
| 	Text name;
 | |
| 	QString phone;
 | |
| 	UserData *contact;
 | |
| };
 | |
| 
 | |
| void initImageLinkManager();
 | |
| void reinitImageLinkManager();
 | |
| void deinitImageLinkManager();
 | |
| 
 | |
| enum ImageLinkType {
 | |
| 	InvalidImageLink = 0,
 | |
| 	YouTubeLink,
 | |
| 	VimeoLink,
 | |
| 	InstagramLink,
 | |
| 	GoogleMapsLink
 | |
| };
 | |
| struct ImageLinkData {
 | |
| 	ImageLinkData(const QString &id) : id(id), type(InvalidImageLink), loading(false) {
 | |
| 	}
 | |
| 
 | |
| 	QString id;
 | |
| 	QString title, duration;
 | |
| 	ImagePtr thumb;
 | |
| 	ImageLinkType type;
 | |
| 	bool loading;
 | |
| 
 | |
| 	void load();
 | |
| };
 | |
| 
 | |
| class ImageLinkManager : public QObject {
 | |
| 	Q_OBJECT
 | |
| public:
 | |
| 	ImageLinkManager() : manager(0), black(0) {
 | |
| 	}
 | |
| 	void init();
 | |
| 	void reinit();
 | |
| 	void deinit();
 | |
| 
 | |
| 	void getData(ImageLinkData *data);
 | |
| 
 | |
| 	~ImageLinkManager() {
 | |
| 		deinit();
 | |
| 	}
 | |
| 
 | |
| public slots:
 | |
| 	void onFinished(QNetworkReply *reply);
 | |
| 	void onFailed(QNetworkReply *reply);
 | |
| 
 | |
| private:
 | |
| 	void failed(ImageLinkData *data);
 | |
| 
 | |
| 	QNetworkAccessManager *manager;
 | |
| 	QMap<QNetworkReply*, ImageLinkData*> dataLoadings, imageLoadings;
 | |
| 	QMap<ImageLinkData*, int32> serverRedirects;
 | |
| 	ImagePtr *black;
 | |
| };
 | |
| 
 | |
| class HistoryImageLink : public HistoryMedia {
 | |
| public:
 | |
| 
 | |
| 	HistoryImageLink(const QString &url, int32 width = 0);
 | |
| 	int32 fullWidth() const;
 | |
| 	int32 fullHeight() const;
 | |
| 	void initDimensions(const HistoryItem *parent);
 | |
| 
 | |
| 	void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	HistoryMediaType type() const {
 | |
| 		return MediaTypeImageLink;
 | |
| 	}
 | |
| 	const QString inDialogsText() const;
 | |
| 	const QString inHistoryText() const;
 | |
| 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 | |
| 	HistoryMedia *clone() const;
 | |
| 
 | |
| private:
 | |
| 	ImageLinkData *data;
 | |
| 	TextLinkPtr link;
 | |
| 
 | |
| };
 | |
| 
 | |
| class HistoryMessage : public HistoryItem {
 | |
| public:
 | |
| 
 | |
| 	HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg);
 | |
| 	HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media);
 | |
| 	HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
 | |
| 	HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc);
 | |
| 
 | |
| 	void initMedia(const MTPMessageMedia &media, QString ¤tText);
 | |
| 	void initMediaFromDocument(DocumentData *doc);
 | |
| 	void initDimensions(const HistoryItem *parent = 0);
 | |
| 	void initDimensions(const QString &text);
 | |
| 	void fromNameUpdated() const;
 | |
| 
 | |
| 	bool uploading() const;
 | |
| 
 | |
| 	void draw(QPainter &p, uint32 selection) const;
 | |
| 	virtual void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
 | |
| 
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	bool hasPoint(int32 x, int32 y) const;
 | |
| 	void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
 | |
| 	void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
 | |
| 	uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
 | |
| 		return _text.adjustSelection(from, to, type);
 | |
| 	}
 | |
| 
 | |
| 	void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
 | |
|     QString notificationHeader() const;
 | |
|     QString notificationText() const;
 | |
|     
 | |
| 	void updateMedia(const MTPMessageMedia &media) {
 | |
| 		if (_media) _media->updateFrom(media);
 | |
| 	}
 | |
| 	void updateStickerEmoji();
 | |
| 
 | |
| 	QString selectedText(uint32 selection) const;
 | |
| 	HistoryMedia *getMedia(bool inOverview = false) const;
 | |
| 
 | |
| 	QString time() const {
 | |
| 		return _time;
 | |
| 	}
 | |
| 	int32 timeWidth() const {
 | |
| 		return _timeWidth;
 | |
| 	}
 | |
| 	virtual bool animating() const {
 | |
| 		return _media ? _media->animating() : false;
 | |
| 	}
 | |
| 
 | |
| 	~HistoryMessage();
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	Text _text;
 | |
| 
 | |
| 	int32 _textWidth, _textHeight;
 | |
| 
 | |
| 	HistoryMedia *_media;
 | |
| 	QString _time;
 | |
| 	int32 _timeWidth;
 | |
| 
 | |
| };
 | |
| 
 | |
| class HistoryForwarded : public HistoryMessage {
 | |
| public:
 | |
| 
 | |
| 	HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessageForwarded &msg);
 | |
| 	HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg);
 | |
| 
 | |
| 	void fwdNameUpdated() const;
 | |
| 
 | |
| 	void draw(QPainter &p, uint32 selection) const;
 | |
| 	void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	bool hasPoint(int32 x, int32 y) const;
 | |
| 	void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
 | |
| 	void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
 | |
| 
 | |
| 	QDateTime dateForwarded() const {
 | |
| 		return fwdDate;
 | |
| 	}
 | |
| 	UserData *fromForwarded() const {
 | |
| 		return fwdFrom;
 | |
| 	}
 | |
| 	QString selectedText(uint32 selection) const;
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	QDateTime fwdDate;
 | |
| 	UserData *fwdFrom;
 | |
| 	mutable Text fwdFromName;
 | |
| 	mutable int32 fwdFromVersion;
 | |
| 	int32 fromWidth;
 | |
| 
 | |
| };
 | |
| 
 | |
| class HistoryServiceMsg : public HistoryItem {
 | |
| public:
 | |
| 
 | |
| 	HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg);
 | |
| 	HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, bool out = false, bool unread = false, HistoryMedia *media = 0);
 | |
| 
 | |
| 	void initDimensions(const HistoryItem *parent = 0);
 | |
| 
 | |
| 	void draw(QPainter &p, uint32 selection) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 	bool hasPoint(int32 x, int32 y) const;
 | |
| 	void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
 | |
| 	void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
 | |
| 	uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
 | |
| 		return _text.adjustSelection(from, to, type);
 | |
| 	}
 | |
| 
 | |
| 	void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
 | |
|     QString notificationText() const;
 | |
| 
 | |
| 	bool needCheck() const {
 | |
| 		return false;
 | |
| 	}
 | |
| 	bool serviceMsg() const {
 | |
| 		return true;
 | |
| 	}
 | |
| 	QString selectedText(uint32 selection) const;
 | |
| 
 | |
| 	HistoryMedia *getMedia(bool inOverview = false) const;
 | |
| 
 | |
| 	virtual bool animating() const {
 | |
| 		return _media ? _media->animating() : false;
 | |
| 	}
 | |
| 
 | |
| 	~HistoryServiceMsg();
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	void setMessageByAction(const MTPmessageAction &action);
 | |
| 
 | |
| 	Text _text;
 | |
| 	HistoryMedia *_media;
 | |
| 
 | |
| 	int32 _textWidth, _textHeight;
 | |
| };
 | |
| 
 | |
| class HistoryDateMsg : public HistoryServiceMsg {
 | |
| public:
 | |
| 
 | |
| 	HistoryDateMsg(History *history, HistoryBlock *block, const QDate &date);
 | |
| 	void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const {
 | |
| 		lnk = TextLinkPtr();
 | |
| 		inText = false;
 | |
| 	}
 | |
| 	void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const {
 | |
| 		symbol = 0xFFFF;
 | |
| 		after = false;
 | |
| 		upon = false;
 | |
| 	}
 | |
| 	QString selectedText(uint32 selection) const {
 | |
| 		return QString();
 | |
| 	}
 | |
| 	int32 itemType() const {
 | |
| 		return DateType;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| HistoryItem *createDayServiceMsg(History *history, HistoryBlock *block, QDateTime date);
 | |
| 
 | |
| class HistoryUnreadBar : public HistoryItem {
 | |
| public:
 | |
| 
 | |
| 	HistoryUnreadBar(History *history, HistoryBlock *block, int32 count, const QDateTime &date);
 | |
| 
 | |
| 	void initDimensions(const HistoryItem *parent = 0);
 | |
| 
 | |
| 	void setCount(int32 count);
 | |
| 
 | |
| 	void draw(QPainter &p, uint32 selection) const;
 | |
| 	int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
 | |
| 
 | |
| 	void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
 | |
|     QString notificationText() const;
 | |
| 
 | |
| 	QString selectedText(uint32 selection) const {
 | |
| 		return QString();
 | |
| 	}
 | |
| 	int32 itemType() const {
 | |
| 		return UnreadBarType;
 | |
| 	}
 | |
| 
 | |
| protected:
 | |
| 
 | |
| 	QString text;
 | |
| 	bool freezed;
 | |
| };
 | 
