mirror of https://github.com/procxx/kepka.git
New profile cover actions by buttons done.
Two new types of Observers: image loaded and async file dialog.
This commit is contained in:
parent
a510bb54ec
commit
46ad43bb1e
|
@ -97,6 +97,10 @@ void ApiWrap::resolveMessageDatas() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::updatesReceived(const MTPUpdates &updates) {
|
||||||
|
App::main()->sentUpdatesReceived(updates);
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
|
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
|
||||||
switch (msgs.type()) {
|
switch (msgs.type()) {
|
||||||
case mtpc_messages_messages: {
|
case mtpc_messages_messages: {
|
||||||
|
@ -679,6 +683,45 @@ void ApiWrap::requestStickerSets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::joinChannel(ChannelData *channel) {
|
||||||
|
if (channel->amIn()) {
|
||||||
|
channelAmInUpdated(channel);
|
||||||
|
} else if (!_channelAmInRequests.contains(channel)) {
|
||||||
|
auto requestId = MTP::send(MTPchannels_JoinChannel(channel->inputChannel), rpcDone(&ApiWrap::channelAmInDone, channel), rpcFail(&ApiWrap::channelAmInFail, channel));
|
||||||
|
_channelAmInRequests.insert(channel, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::leaveChannel(ChannelData *channel) {
|
||||||
|
if (!channel->amIn()) {
|
||||||
|
channelAmInUpdated(channel);
|
||||||
|
} else if (!_channelAmInRequests.contains(channel)) {
|
||||||
|
auto requestId = MTP::send(MTPchannels_LeaveChannel(channel->inputChannel), rpcDone(&ApiWrap::channelAmInDone, channel), rpcFail(&ApiWrap::channelAmInFail, channel));
|
||||||
|
_channelAmInRequests.insert(channel, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::channelAmInUpdated(ChannelData *channel) {
|
||||||
|
Notify::PeerUpdate update(channel);
|
||||||
|
update.flags |= Notify::PeerUpdateFlag::ChannelAmIn;
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
|
Notify::peerUpdatedSendDelayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::channelAmInDone(ChannelData *channel, const MTPUpdates &updates) {
|
||||||
|
_channelAmInRequests.remove(channel);
|
||||||
|
|
||||||
|
updatesReceived(updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::channelAmInFail(ChannelData *channel, const RPCError &error) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
_channelAmInRequests.remove(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
|
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
|
||||||
_stickerSetRequests.remove(setId);
|
_stickerSetRequests.remove(setId);
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
ApiWrap(QObject *parent);
|
ApiWrap(QObject *parent);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
typedef SharedCallback<void, ChannelData*, MsgId> RequestMessageDataCallback;
|
using RequestMessageDataCallback = SharedCallback<void, ChannelData*, MsgId>;
|
||||||
void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
|
void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
|
||||||
|
|
||||||
void requestFullPeer(PeerData *peer);
|
void requestFullPeer(PeerData *peer);
|
||||||
|
@ -50,6 +50,9 @@ public:
|
||||||
void scheduleStickerSetRequest(uint64 setId, uint64 access);
|
void scheduleStickerSetRequest(uint64 setId, uint64 access);
|
||||||
void requestStickerSets();
|
void requestStickerSets();
|
||||||
|
|
||||||
|
void joinChannel(ChannelData *channel);
|
||||||
|
void leaveChannel(ChannelData *channel);
|
||||||
|
|
||||||
~ApiWrap();
|
~ApiWrap();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -65,6 +68,8 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void updatesReceived(const MTPUpdates &updates);
|
||||||
|
|
||||||
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
|
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
|
||||||
struct MessageDataRequest {
|
struct MessageDataRequest {
|
||||||
MessageDataRequest() : req(0) {
|
MessageDataRequest() : req(0) {
|
||||||
|
@ -120,4 +125,9 @@ private:
|
||||||
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
|
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
|
||||||
bool gotStickerSetFail(uint64 setId, const RPCError &error);
|
bool gotStickerSetFail(uint64 setId, const RPCError &error);
|
||||||
|
|
||||||
|
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
|
||||||
|
void channelAmInUpdated(ChannelData *channel);
|
||||||
|
void channelAmInDone(ChannelData *channel, const MTPUpdates &updates);
|
||||||
|
bool channelAmInFail(ChannelData *channel, const RPCError &error);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -369,7 +369,7 @@ namespace {
|
||||||
UserData *result = nullptr;
|
UserData *result = nullptr;
|
||||||
for_const (auto &user, users.c_vector().v) {
|
for_const (auto &user, users.c_vector().v) {
|
||||||
UserData *data = nullptr;
|
UserData *data = nullptr;
|
||||||
bool wasContact = false, canShareContact = false, minimal = false;
|
bool wasContact = false, minimal = false;
|
||||||
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
|
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
|
||||||
|
|
||||||
Notify::PeerUpdate update;
|
Notify::PeerUpdate update;
|
||||||
|
@ -548,12 +548,13 @@ namespace {
|
||||||
|
|
||||||
switch (chat.type()) {
|
switch (chat.type()) {
|
||||||
case mtpc_chat: {
|
case mtpc_chat: {
|
||||||
const auto &d(chat.c_chat());
|
auto &d(chat.c_chat());
|
||||||
|
|
||||||
data = App::chat(peerFromChat(d.vid.v));
|
data = App::chat(peerFromChat(d.vid.v));
|
||||||
data->input = MTP_inputPeerChat(d.vid);
|
auto cdata = data->asChat();
|
||||||
|
auto canEdit = cdata->canEdit();
|
||||||
|
|
||||||
ChatData *cdata = data->asChat();
|
data->input = MTP_inputPeerChat(d.vid);
|
||||||
cdata->setNameDelayed(qs(d.vtitle));
|
cdata->setNameDelayed(qs(d.vtitle));
|
||||||
cdata->setPhoto(d.vphoto);
|
cdata->setPhoto(d.vphoto);
|
||||||
cdata->date = d.vdate.v;
|
cdata->date = d.vdate.v;
|
||||||
|
@ -604,14 +605,18 @@ namespace {
|
||||||
cdata->version = d.vversion.v;
|
cdata->version = d.vversion.v;
|
||||||
cdata->invalidateParticipants();
|
cdata->invalidateParticipants();
|
||||||
}
|
}
|
||||||
|
if (canEdit != cdata->canEdit()) {
|
||||||
|
update.flags |= UpdateFlag::ChatCanEdit;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case mtpc_chatForbidden: {
|
case mtpc_chatForbidden: {
|
||||||
const auto &d(chat.c_chatForbidden());
|
auto &d(chat.c_chatForbidden());
|
||||||
|
|
||||||
data = App::chat(peerFromChat(d.vid.v));
|
data = App::chat(peerFromChat(d.vid.v));
|
||||||
data->input = MTP_inputPeerChat(d.vid);
|
auto cdata = data->asChat();
|
||||||
|
auto canEdit = cdata->canEdit();
|
||||||
|
|
||||||
ChatData *cdata = data->asChat();
|
data->input = MTP_inputPeerChat(d.vid);
|
||||||
cdata->setNameDelayed(qs(d.vtitle));
|
cdata->setNameDelayed(qs(d.vtitle));
|
||||||
cdata->setPhoto(MTP_chatPhotoEmpty());
|
cdata->setPhoto(MTP_chatPhotoEmpty());
|
||||||
cdata->date = 0;
|
cdata->date = 0;
|
||||||
|
@ -619,27 +624,32 @@ namespace {
|
||||||
cdata->invalidateParticipants();
|
cdata->invalidateParticipants();
|
||||||
cdata->flags = 0;
|
cdata->flags = 0;
|
||||||
cdata->isForbidden = true;
|
cdata->isForbidden = true;
|
||||||
|
if (canEdit != cdata->canEdit()) {
|
||||||
|
update.flags |= UpdateFlag::ChatCanEdit;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case mtpc_channel: {
|
case mtpc_channel: {
|
||||||
const auto &d(chat.c_channel());
|
auto &d(chat.c_channel());
|
||||||
|
|
||||||
PeerId peer(peerFromChannel(d.vid.v));
|
auto peerId = peerFromChannel(d.vid.v);
|
||||||
minimal = d.is_min();
|
minimal = d.is_min();
|
||||||
if (minimal) {
|
if (minimal) {
|
||||||
data = App::channelLoaded(peer);
|
data = App::channelLoaded(peerId);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
continue; // minimal is not loaded, need to make getDifference
|
continue; // minimal is not loaded, need to make getDifference
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data = App::channel(peer);
|
data = App::channel(peerId);
|
||||||
data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0));
|
data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelData *cdata = data->asChannel();
|
auto cdata = data->asChannel();
|
||||||
auto wasInChannel = cdata->amIn();
|
auto wasInChannel = cdata->amIn();
|
||||||
|
auto canEditPhoto = cdata->canEditPhoto();
|
||||||
|
auto canAddMembers = cdata->canAddParticipants();
|
||||||
|
|
||||||
if (minimal) {
|
if (minimal) {
|
||||||
int32 mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
|
MTPDchannel::Flags mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
|
||||||
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
|
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
|
||||||
} else {
|
} else {
|
||||||
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
||||||
|
@ -662,19 +672,27 @@ namespace {
|
||||||
cdata->flagsUpdated();
|
cdata->flagsUpdated();
|
||||||
cdata->setPhoto(d.vphoto);
|
cdata->setPhoto(d.vphoto);
|
||||||
|
|
||||||
if (wasInChannel != cdata->amIn() && !cdata->isMegagroup()) {
|
if (wasInChannel != cdata->amIn()) {
|
||||||
update.flags |= UpdateFlag::ChannelAmIn;
|
update.flags |= UpdateFlag::ChannelAmIn;
|
||||||
}
|
}
|
||||||
|
if (canEditPhoto != cdata->canEditPhoto()) {
|
||||||
|
update.flags |= UpdateFlag::ChannelCanEditPhoto;
|
||||||
|
}
|
||||||
|
if (canAddMembers != cdata->canAddParticipants()) {
|
||||||
|
update.flags |= UpdateFlag::ChannelCanAddMembers;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case mtpc_channelForbidden: {
|
case mtpc_channelForbidden: {
|
||||||
const auto &d(chat.c_channelForbidden());
|
auto &d(chat.c_channelForbidden());
|
||||||
|
|
||||||
PeerId peer(peerFromChannel(d.vid.v));
|
auto peerId = peerFromChannel(d.vid.v);
|
||||||
data = App::channel(peer);
|
data = App::channel(peerId);
|
||||||
data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash);
|
data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash);
|
||||||
|
|
||||||
ChannelData *cdata = data->asChannel();
|
auto cdata = data->asChannel();
|
||||||
auto wasInChannel = cdata->amIn();
|
auto wasInChannel = cdata->amIn();
|
||||||
|
auto canEditPhoto = cdata->canEditPhoto();
|
||||||
|
auto canAddMembers = cdata->canAddParticipants();
|
||||||
|
|
||||||
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
||||||
|
|
||||||
|
@ -686,9 +704,15 @@ namespace {
|
||||||
cdata->count = 0;
|
cdata->count = 0;
|
||||||
cdata->isForbidden = true;
|
cdata->isForbidden = true;
|
||||||
|
|
||||||
if (wasInChannel != cdata->amIn() && !cdata->isMegagroup()) {
|
if (wasInChannel != cdata->amIn()) {
|
||||||
update.flags |= UpdateFlag::ChannelAmIn;
|
update.flags |= UpdateFlag::ChannelAmIn;
|
||||||
}
|
}
|
||||||
|
if (canEditPhoto != cdata->canEditPhoto()) {
|
||||||
|
update.flags |= UpdateFlag::ChannelCanEditPhoto;
|
||||||
|
}
|
||||||
|
if (canAddMembers != cdata->canAddParticipants()) {
|
||||||
|
update.flags |= UpdateFlag::ChannelCanAddMembers;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
if (!data) continue;
|
if (!data) continue;
|
||||||
|
|
|
@ -27,10 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
#include "boxes/confirmbox.h"
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "ui/filedialog.h"
|
||||||
#include "langloaderplain.h"
|
#include "langloaderplain.h"
|
||||||
#include "localstorage.h"
|
#include "localstorage.h"
|
||||||
#include "autoupdater.h"
|
#include "autoupdater.h"
|
||||||
#include "core/observer.h"
|
#include "core/observer.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void mtpStateChanged(int32 dc, int32 state) {
|
void mtpStateChanged(int32 dc, int32 state) {
|
||||||
|
@ -817,6 +819,7 @@ void AppClass::doMtpUnpause() {
|
||||||
void AppClass::selfPhotoCleared(const MTPUserProfilePhoto &result) {
|
void AppClass::selfPhotoCleared(const MTPUserProfilePhoto &result) {
|
||||||
if (!App::self()) return;
|
if (!App::self()) return;
|
||||||
App::self()->setPhoto(result);
|
App::self()->setPhoto(result);
|
||||||
|
Notify::peerUpdatedSendDelayed();
|
||||||
emit peerPhotoDone(App::self()->id);
|
emit peerPhotoDone(App::self()->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,6 +909,14 @@ void AppClass::call_handleUnreadCounterUpdate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppClass::call_handleFileDialogQueue() {
|
||||||
|
while (true) {
|
||||||
|
if (!FileDialog::processQuery()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AppClass::killDownloadSessions() {
|
void AppClass::killDownloadSessions() {
|
||||||
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
|
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
|
||||||
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
||||||
|
|
|
@ -203,6 +203,7 @@ public slots:
|
||||||
|
|
||||||
void call_handleHistoryUpdate();
|
void call_handleHistoryUpdate();
|
||||||
void call_handleUnreadCounterUpdate();
|
void call_handleUnreadCounterUpdate();
|
||||||
|
void call_handleFileDialogQueue();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -2003,11 +2003,11 @@ MembersFilter MembersInner::filter() const {
|
||||||
return _filter;
|
return _filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<UserData*, bool> MembersInner::already() const {
|
MembersAlreadyIn MembersInner::already() const {
|
||||||
MembersAlreadyIn result;
|
MembersAlreadyIn result;
|
||||||
for (int32 i = 0, l = _rows.size(); i < l; ++i) {
|
for_const (auto peer, _rows) {
|
||||||
if (_rows.at(i)->isUser()) {
|
if (peer->isUser()) {
|
||||||
result.insert(_rows.at(i)->asUser(), true);
|
result.insert(peer->asUser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -31,7 +31,7 @@ enum MembersFilter {
|
||||||
MembersFilterRecent,
|
MembersFilterRecent,
|
||||||
MembersFilterAdmins,
|
MembersFilterAdmins,
|
||||||
};
|
};
|
||||||
typedef QMap<UserData*, bool> MembersAlreadyIn;
|
using MembersAlreadyIn = OrderedSet<UserData*>;
|
||||||
|
|
||||||
QString cantInviteError();
|
QString cantInviteError();
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ public:
|
||||||
}
|
}
|
||||||
void clearSel();
|
void clearSel();
|
||||||
|
|
||||||
QMap<UserData*, bool> already() const;
|
MembersAlreadyIn already() const;
|
||||||
|
|
||||||
~MembersInner();
|
~MembersInner();
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,13 @@ T *getPointerAndReset(T *&ptr) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T createAndSwap(T &value) {
|
||||||
|
T result;
|
||||||
|
std::swap(result, value);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
struct NullType {
|
struct NullType {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ NeverFreedPointer<StartCallbacksList> StartCallbacks;
|
||||||
NeverFreedPointer<FinishCallbacksList> FinishCallbacks;
|
NeverFreedPointer<FinishCallbacksList> FinishCallbacks;
|
||||||
UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/;
|
UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/;
|
||||||
|
|
||||||
|
ObservedEvent LastRegisteredEvent/* = 0*/;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void startObservers() {
|
void startObservers() {
|
||||||
|
@ -50,17 +52,18 @@ void finishObservers() {
|
||||||
FinishCallbacks.clear();
|
FinishCallbacks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ObservedEventRegistrator::ObservedEventRegistrator(ObservedEvent event
|
ObservedEventRegistrator::ObservedEventRegistrator(StartObservedEventCallback startCallback
|
||||||
, StartObservedEventCallback startCallback
|
|
||||||
, FinishObservedEventCallback finishCallback
|
, FinishObservedEventCallback finishCallback
|
||||||
, UnregisterObserverCallback unregisterCallback) {
|
, UnregisterObserverCallback unregisterCallback) {
|
||||||
|
_event = LastRegisteredEvent++;
|
||||||
|
|
||||||
StartCallbacks.makeIfNull();
|
StartCallbacks.makeIfNull();
|
||||||
StartCallbacks->push_back(startCallback);
|
StartCallbacks->push_back(startCallback);
|
||||||
|
|
||||||
FinishCallbacks.makeIfNull();
|
FinishCallbacks.makeIfNull();
|
||||||
FinishCallbacks->push_back(finishCallback);
|
FinishCallbacks->push_back(finishCallback);
|
||||||
|
|
||||||
UnregisterCallbacks[event] = unregisterCallback;
|
UnregisterCallbacks[_event] = unregisterCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observer base interface.
|
// Observer base interface.
|
||||||
|
|
|
@ -42,10 +42,16 @@ using FinishObservedEventCallback = void(*)();
|
||||||
// unregisterCallback will be used to destroy connections.
|
// unregisterCallback will be used to destroy connections.
|
||||||
class ObservedEventRegistrator {
|
class ObservedEventRegistrator {
|
||||||
public:
|
public:
|
||||||
ObservedEventRegistrator(ObservedEvent event
|
ObservedEventRegistrator(StartObservedEventCallback startCallback,
|
||||||
, StartObservedEventCallback startCallback
|
FinishObservedEventCallback finishCallback,
|
||||||
, FinishObservedEventCallback finishCallback
|
UnregisterObserverCallback unregisterCallback);
|
||||||
, UnregisterObserverCallback unregisterCallback);
|
|
||||||
|
inline ObservedEvent event() const {
|
||||||
|
return _event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ObservedEvent _event;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,8 +94,12 @@ struct ObserversList {
|
||||||
QVector<int> freeIndices;
|
QVector<int> freeIndices;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If no filtering by flags is done, you can use this value in both
|
||||||
|
// Notify::registerObserver() and Notify::notifyObservers()
|
||||||
|
constexpr int UniversalFlag = 0x01;
|
||||||
|
|
||||||
template <typename Flags, typename Handler>
|
template <typename Flags, typename Handler>
|
||||||
int registerObserver(ObserversList<Flags, Handler> &list, Flags flags, Handler &&handler) {
|
ConnectionId registerObserver(ObservedEvent event, ObserversList<Flags, Handler> &list, Flags flags, Handler &&handler) {
|
||||||
while (!list.freeIndices.isEmpty()) {
|
while (!list.freeIndices.isEmpty()) {
|
||||||
auto freeIndex = list.freeIndices.back();
|
auto freeIndex = list.freeIndices.back();
|
||||||
list.freeIndices.pop_back();
|
list.freeIndices.pop_back();
|
||||||
|
@ -100,7 +110,8 @@ int registerObserver(ObserversList<Flags, Handler> &list, Flags flags, Handler &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.entries.push_back({ flags, std_::move(handler) });
|
list.entries.push_back({ flags, std_::move(handler) });
|
||||||
return list.entries.size() - 1;
|
int connectionIndex = list.entries.size() - 1;
|
||||||
|
return (static_cast<uint32>(event) << 24) | static_cast<uint32>(connectionIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Flags, typename Handler>
|
template <typename Flags, typename Handler>
|
||||||
|
@ -131,17 +142,26 @@ namespace internal {
|
||||||
|
|
||||||
template <typename ObserverType, int>
|
template <typename ObserverType, int>
|
||||||
struct ObserverRegisteredGeneric {
|
struct ObserverRegisteredGeneric {
|
||||||
static void call(ObserverType *observer, ConnectionId connection) {
|
static inline void call(ObserverType *observer, ConnectionId connection) {
|
||||||
observer->observerRegistered(connection);
|
observer->observerRegistered(connection);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObserverType>
|
template <typename ObserverType>
|
||||||
struct ObserverRegisteredGeneric<ObserverType, true> {
|
struct ObserverRegisteredGeneric<ObserverType, true> {
|
||||||
static void call(ObserverType *observer, ConnectionId connection) {
|
static inline void call(ObserverType *observer, ConnectionId connection) {
|
||||||
observerRegisteredDefault(observer, connection);
|
observerRegisteredDefault(observer, connection);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
inline void observerRegistered(ObserverType *observer, ConnectionId connection) {
|
||||||
|
// For derivatives of the Observer class we call special friend function observerRegistered().
|
||||||
|
// For all other classes we call just a member function observerRegistered().
|
||||||
|
using ObserverRegistered = internal::ObserverRegisteredGeneric<ObserverType, std_::is_base_of<Observer, ObserverType>::value>;
|
||||||
|
ObserverRegistered::call(observer, connection);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Notify
|
} // namespace Notify
|
||||||
|
|
|
@ -519,6 +519,7 @@ struct Data {
|
||||||
uint64 LaunchId = 0;
|
uint64 LaunchId = 0;
|
||||||
SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" };
|
SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" };
|
||||||
SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" };
|
SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" };
|
||||||
|
SingleDelayedCall HandleFileDialogQueue = { App::app(), "call_handleFileDialogQueue" };
|
||||||
|
|
||||||
Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout;
|
Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout;
|
||||||
bool AdaptiveForWide = true;
|
bool AdaptiveForWide = true;
|
||||||
|
@ -582,6 +583,7 @@ void finish() {
|
||||||
DefineReadOnlyVar(Global, uint64, LaunchId);
|
DefineReadOnlyVar(Global, uint64, LaunchId);
|
||||||
DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate);
|
DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate);
|
||||||
DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate);
|
DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate);
|
||||||
|
DefineRefVar(Global, SingleDelayedCall, HandleFileDialogQueue);
|
||||||
|
|
||||||
DefineVar(Global, Adaptive::Layout, AdaptiveLayout);
|
DefineVar(Global, Adaptive::Layout, AdaptiveLayout);
|
||||||
DefineVar(Global, bool, AdaptiveForWide);
|
DefineVar(Global, bool, AdaptiveForWide);
|
||||||
|
|
|
@ -204,6 +204,7 @@ void finish();
|
||||||
DeclareReadOnlyVar(uint64, LaunchId);
|
DeclareReadOnlyVar(uint64, LaunchId);
|
||||||
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
|
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
|
||||||
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
|
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
|
||||||
|
DeclareRefVar(SingleDelayedCall, HandleFileDialogQueue);
|
||||||
|
|
||||||
DeclareVar(Adaptive::Layout, AdaptiveLayout);
|
DeclareVar(Adaptive::Layout, AdaptiveLayout);
|
||||||
DeclareVar(bool, AdaptiveForWide);
|
DeclareVar(bool, AdaptiveForWide);
|
||||||
|
|
|
@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "mtproto/core_types.h"
|
#include "mtproto/core_types.h"
|
||||||
#include "mtproto/session.h"
|
#include "mtproto/session.h"
|
||||||
#include "mtproto/file_download.h"
|
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,7 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &
|
||||||
}
|
}
|
||||||
emit App::wnd()->imageLoaded();
|
emit App::wnd()->imageLoaded();
|
||||||
emit progress(this);
|
emit progress(this);
|
||||||
|
FileDownload::internal::notifyImageLoaded();
|
||||||
loadNext();
|
loadNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,6 +550,9 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
|
||||||
if (DebugLogging::FileLoader() && _id) DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests)));
|
if (DebugLogging::FileLoader() && _id) DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests)));
|
||||||
}
|
}
|
||||||
emit progress(this);
|
emit progress(this);
|
||||||
|
if (_complete) {
|
||||||
|
FileDownload::internal::notifyImageLoaded();
|
||||||
|
}
|
||||||
loadNext();
|
loadNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,6 +682,7 @@ void webFileLoader::onFinished(const QByteArray &data) {
|
||||||
Local::writeWebFile(_url, _data);
|
Local::writeWebFile(_url, _data);
|
||||||
}
|
}
|
||||||
emit progress(this);
|
emit progress(this);
|
||||||
|
FileDownload::internal::notifyImageLoaded();
|
||||||
loadNext();
|
loadNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1089,3 +1094,45 @@ namespace MTP {
|
||||||
++GlobalPriority;
|
++GlobalPriority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace FileDownload {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using internal::ImageLoadedHandler;
|
||||||
|
|
||||||
|
using ImageLoadedObserversList = Notify::ObserversList<int, ImageLoadedHandler>;
|
||||||
|
NeverFreedPointer<ImageLoadedObserversList> ImageLoadedObservers;
|
||||||
|
|
||||||
|
void StartCallback() {
|
||||||
|
ImageLoadedObservers.makeIfNull();
|
||||||
|
}
|
||||||
|
void FinishCallback() {
|
||||||
|
ImageLoadedObservers.clear();
|
||||||
|
}
|
||||||
|
void UnregisterCallback(int connectionIndex) {
|
||||||
|
t_assert(!ImageLoadedObservers.isNull());
|
||||||
|
Notify::unregisterObserver(*ImageLoadedObservers, connectionIndex);
|
||||||
|
}
|
||||||
|
Notify::ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback);
|
||||||
|
|
||||||
|
bool Started() {
|
||||||
|
return !ImageLoadedObservers.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler) {
|
||||||
|
t_assert(Started());
|
||||||
|
auto connectionId = Notify::registerObserver(creator.event(), *ImageLoadedObservers
|
||||||
|
, Notify::UniversalFlag, std_::forward<ImageLoadedHandler>(handler));
|
||||||
|
return connectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyImageLoaded() {
|
||||||
|
Notify::notifyObservers(*ImageLoadedObservers, Notify::UniversalFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
}
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
void clearLoaderPriorities();
|
void clearLoaderPriorities();
|
||||||
}
|
}
|
||||||
|
@ -391,3 +393,21 @@ static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation<WebL
|
||||||
|
|
||||||
void reinitWebLoadManager();
|
void reinitWebLoadManager();
|
||||||
void stopWebLoadManager();
|
void stopWebLoadManager();
|
||||||
|
|
||||||
|
namespace FileDownload {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
using ImageLoadedHandler = Function<void>;
|
||||||
|
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler);
|
||||||
|
|
||||||
|
void notifyImageLoaded();
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
void registerImageLoadedObserver(ObserverType *observer, void (ObserverType::*handler)()) {
|
||||||
|
auto connection = internal::plainRegisterImageLoadedObserver(func(observer, handler));
|
||||||
|
Notify::observerRegistered(observer, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileDownload
|
||||||
|
|
|
@ -32,7 +32,6 @@ namespace Notify {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr ObservedEvent PeerUpdateEvent = 0x01;
|
|
||||||
using PeerObserversList = ObserversList<PeerUpdateFlags, PeerUpdateHandler>;
|
using PeerObserversList = ObserversList<PeerUpdateFlags, PeerUpdateHandler>;
|
||||||
NeverFreedPointer<PeerObserversList> PeerUpdateObservers;
|
NeverFreedPointer<PeerObserversList> PeerUpdateObservers;
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ void UnregisterCallback(int connectionIndex) {
|
||||||
t_assert(!PeerUpdateObservers.isNull());
|
t_assert(!PeerUpdateObservers.isNull());
|
||||||
unregisterObserver(*PeerUpdateObservers, connectionIndex);
|
unregisterObserver(*PeerUpdateObservers, connectionIndex);
|
||||||
}
|
}
|
||||||
ObservedEventRegistrator creator(PeerUpdateEvent, StartCallback, FinishCallback, UnregisterCallback);
|
ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback);
|
||||||
|
|
||||||
bool Started() {
|
bool Started() {
|
||||||
return !PeerUpdateObservers.isNull();
|
return !PeerUpdateObservers.isNull();
|
||||||
|
@ -65,9 +64,9 @@ bool Started() {
|
||||||
|
|
||||||
ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) {
|
ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) {
|
||||||
t_assert(Started());
|
t_assert(Started());
|
||||||
auto connectionId = registerObserver(*PeerUpdateObservers, events, std_::forward<PeerUpdateHandler>(handler));
|
auto connectionId = registerObserver(creator.event(), *PeerUpdateObservers
|
||||||
t_assert(connectionId >= 0 && connectionId < 0x01000000);
|
, events, std_::forward<PeerUpdateHandler>(handler));
|
||||||
return (static_cast<uint32>(PeerUpdateEvent) << 24) | static_cast<uint32>(connectionId + 1);
|
return connectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
|
void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
|
||||||
|
@ -116,10 +115,8 @@ void peerUpdatedSendDelayed() {
|
||||||
|
|
||||||
if (internal::SmallUpdates->isEmpty()) return;
|
if (internal::SmallUpdates->isEmpty()) return;
|
||||||
|
|
||||||
internal::SmallUpdatesList smallList;
|
auto smallList = createAndSwap(*internal::SmallUpdates);
|
||||||
internal::AllUpdatesList allList;
|
auto allList = createAndSwap(*internal::AllUpdates);
|
||||||
std::swap(smallList, *internal::SmallUpdates);
|
|
||||||
std::swap(allList, *internal::AllUpdates);
|
|
||||||
for_const (auto &update, smallList) {
|
for_const (auto &update, smallList) {
|
||||||
notifyObservers(*internal::PeerUpdateObservers, update.flags, update);
|
notifyObservers(*internal::PeerUpdateObservers, update.flags, update);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,15 @@ namespace Notify {
|
||||||
enum class PeerUpdateFlag {
|
enum class PeerUpdateFlag {
|
||||||
NameChanged = 0x00000001U,
|
NameChanged = 0x00000001U,
|
||||||
UsernameChanged = 0x00000002U,
|
UsernameChanged = 0x00000002U,
|
||||||
|
PhotoChanged = 0x00000004U,
|
||||||
|
|
||||||
UserCanShareContact = 0x00010000U,
|
UserCanShareContact = 0x00010000U,
|
||||||
|
|
||||||
ChatCanEdit = 0x00010000U,
|
ChatCanEdit = 0x00010000U,
|
||||||
|
|
||||||
ChannelAmIn = 0x00010000U,
|
ChannelAmIn = 0x00010000U,
|
||||||
MegagroupCanEditPhoto = 0x00020000U,
|
ChannelCanEditPhoto = 0x00020000U,
|
||||||
MegagroupCanAddMembers = 0x00040000U,
|
ChannelCanAddMembers = 0x00040000U,
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(PeerUpdateFlags, PeerUpdateFlag);
|
Q_DECLARE_FLAGS(PeerUpdateFlags, PeerUpdateFlag);
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdateFlags);
|
Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdateFlags);
|
||||||
|
@ -68,11 +69,7 @@ ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler
|
||||||
template <typename ObserverType>
|
template <typename ObserverType>
|
||||||
void registerPeerObserver(PeerUpdateFlags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) {
|
void registerPeerObserver(PeerUpdateFlags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) {
|
||||||
auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler));
|
auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler));
|
||||||
|
observerRegistered(observer, connection);
|
||||||
// For derivatives of the Observer class we call special friend function observerRegistered().
|
|
||||||
// For all other classes we call just a member function observerRegistered().
|
|
||||||
using ObserverRegistered = internal::ObserverRegisteredGeneric<ObserverType, std_::is_base_of<Observer, ObserverType>::value>;
|
|
||||||
ObserverRegistered::call(observer, connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Notify
|
} // namespace Notify
|
||||||
|
|
|
@ -23,8 +23,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "styles/style_profile.h"
|
#include "styles/style_profile.h"
|
||||||
#include "ui/buttons/round_button.h"
|
#include "ui/buttons/round_button.h"
|
||||||
|
#include "ui/filedialog.h"
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
|
#include "boxes/confirmbox.h"
|
||||||
#include "boxes/contactsbox.h"
|
#include "boxes/contactsbox.h"
|
||||||
|
#include "boxes/photocropbox.h"
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
@ -62,31 +65,61 @@ private:
|
||||||
|
|
||||||
const Notify::PeerUpdateFlags ButtonsUpdateFlags = Notify::PeerUpdateFlag::UserCanShareContact
|
const Notify::PeerUpdateFlags ButtonsUpdateFlags = Notify::PeerUpdateFlag::UserCanShareContact
|
||||||
| Notify::PeerUpdateFlag::ChatCanEdit
|
| Notify::PeerUpdateFlag::ChatCanEdit
|
||||||
| Notify::PeerUpdateFlag::MegagroupCanEditPhoto
|
| Notify::PeerUpdateFlag::ChannelCanEditPhoto
|
||||||
| Notify::PeerUpdateFlag::MegagroupCanAddMembers
|
| Notify::PeerUpdateFlag::ChannelCanAddMembers
|
||||||
| Notify::PeerUpdateFlag::ChannelAmIn;
|
| Notify::PeerUpdateFlag::ChannelAmIn;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class PhotoButton final : public Button {
|
class PhotoButton final : public Button, public Notify::Observer {
|
||||||
public:
|
public:
|
||||||
PhotoButton(QWidget *parent, PeerData *peer) : Button(parent), _peer(peer) {
|
PhotoButton(QWidget *parent, PeerData *peer) : Button(parent), _peer(peer) {
|
||||||
resize(st::profilePhotoSize, st::profilePhotoSize);
|
resize(st::profilePhotoSize, st::profilePhotoSize);
|
||||||
}
|
|
||||||
void photoUpdated() {
|
processNewPeerPhoto();
|
||||||
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
|
|
||||||
setCursor(hasPhoto ? style::cur_pointer : style::cur_default);
|
Notify::registerPeerObserver(Notify::PeerUpdateFlag::PhotoChanged, this, &PhotoButton::notifyPeerUpdated);
|
||||||
|
FileDownload::registerImageLoadedObserver(this, &PhotoButton::notifyImageLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) {
|
void paintEvent(QPaintEvent *e) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
p.drawPixmap(0, 0, _userpic);
|
||||||
_peer->paintUserpic(p, st::profilePhotoSize, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer != _peer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processNewPeerPhoto();
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyImageLoaded() {
|
||||||
|
if (_waiting && _peer->userpicLoaded()) {
|
||||||
|
_waiting = false;
|
||||||
|
_userpic = _peer->genUserpic(st::profilePhotoSize);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processNewPeerPhoto() {
|
||||||
|
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
|
||||||
|
setCursor(hasPhoto ? style::cur_pointer : style::cur_default);
|
||||||
|
_waiting = !_peer->userpicLoaded();
|
||||||
|
if (_waiting) {
|
||||||
|
_peer->loadUserpic(true);
|
||||||
|
} else {
|
||||||
|
_userpic = _peer->genUserpic(st::profilePhotoSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PeerData *_peer;
|
PeerData *_peer;
|
||||||
|
bool _waiting = false;
|
||||||
|
QPixmap _userpic;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,8 +134,8 @@ CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||||
|
|
||||||
auto observeEvents = ButtonsUpdateFlags | Notify::PeerUpdateFlag::NameChanged;
|
auto observeEvents = ButtonsUpdateFlags | Notify::PeerUpdateFlag::NameChanged;
|
||||||
Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
|
Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
|
||||||
|
FileDialog::registerObserver(this, &CoverWidget::notifyFileQueryUpdated);
|
||||||
|
|
||||||
_photoButton->photoUpdated();
|
|
||||||
connect(_photoButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
|
connect(_photoButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
|
||||||
|
|
||||||
refreshNameText();
|
refreshNameText();
|
||||||
|
@ -132,12 +165,9 @@ void CoverWidget::resizeToWidth(int newWidth) {
|
||||||
_statusPosition = QPoint(infoLeft + st::profileStatusLeft, _photoButton->y() + st::profileStatusTop);
|
_statusPosition = QPoint(infoLeft + st::profileStatusLeft, _photoButton->y() + st::profileStatusTop);
|
||||||
|
|
||||||
int buttonLeft = st::profilePhotoLeft + _photoButton->width() + st::profileButtonLeft;
|
int buttonLeft = st::profilePhotoLeft + _photoButton->width() + st::profileButtonLeft;
|
||||||
if (_primaryButton) {
|
for_const (auto button, _buttons) {
|
||||||
_primaryButton->moveToLeft(buttonLeft, st::profileButtonTop);
|
button->moveToLeft(buttonLeft, st::profileButtonTop);
|
||||||
buttonLeft += _primaryButton->width() + st::profileButtonSkip;
|
buttonLeft += button->width() + st::profileButtonSkip;
|
||||||
}
|
|
||||||
if (_secondaryButton) {
|
|
||||||
_secondaryButton->moveToLeft(buttonLeft, st::profileButtonTop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newHeight += st::profilePhotoSize;
|
newHeight += st::profilePhotoSize;
|
||||||
|
@ -178,7 +208,9 @@ void CoverWidget::paintDivider(Painter &p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
if (update.peer == _peer) {
|
if (update.peer != _peer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ((update.flags & ButtonsUpdateFlags) != 0) {
|
if ((update.flags & ButtonsUpdateFlags) != 0) {
|
||||||
refreshButtons();
|
refreshButtons();
|
||||||
}
|
}
|
||||||
|
@ -186,7 +218,6 @@ void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
refreshNameText();
|
refreshNameText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void CoverWidget::refreshNameText() {
|
void CoverWidget::refreshNameText() {
|
||||||
_nameText.setText(st::profileNameFont, App::peerName(_peer));
|
_nameText.setText(st::profileNameFont, App::peerName(_peer));
|
||||||
|
@ -247,6 +278,7 @@ bool CoverWidget::isUsingMegagroupOnlineCount() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::refreshButtons() {
|
void CoverWidget::refreshButtons() {
|
||||||
|
clearButtons();
|
||||||
if (_peerUser) {
|
if (_peerUser) {
|
||||||
setUserButtons();
|
setUserButtons();
|
||||||
} else if (_peerChat) {
|
} else if (_peerChat) {
|
||||||
|
@ -260,65 +292,51 @@ void CoverWidget::refreshButtons() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::setUserButtons() {
|
void CoverWidget::setUserButtons() {
|
||||||
setPrimaryButton(lang(lng_profile_send_message), SLOT(onSendMessage()));
|
addButton(lang(lng_profile_send_message), SLOT(onSendMessage()));
|
||||||
if (_peerUser->canShareThisContact()) {
|
if (_peerUser->canShareThisContact()) {
|
||||||
setSecondaryButton(lang(lng_profile_share_contact), SLOT(onShareContact()));
|
addButton(lang(lng_profile_share_contact), SLOT(onShareContact()));
|
||||||
} else {
|
|
||||||
clearSecondaryButton();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::setChatButtons() {
|
void CoverWidget::setChatButtons() {
|
||||||
if (_peerChat->canEdit()) {
|
if (_peerChat->canEdit()) {
|
||||||
setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||||
setSecondaryButton(lang(lng_profile_add_participant), SLOT(onAddMember()));
|
addButton(lang(lng_profile_add_participant), SLOT(onAddMember()));
|
||||||
} else {
|
|
||||||
clearPrimaryButton();
|
|
||||||
clearSecondaryButton();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::setMegagroupButtons() {
|
void CoverWidget::setMegagroupButtons() {
|
||||||
if (_peerMegagroup->canEditPhoto()) {
|
if (_peerMegagroup->canEditPhoto()) {
|
||||||
setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||||
} else {
|
|
||||||
clearPrimaryButton();
|
|
||||||
}
|
}
|
||||||
if (_peerMegagroup->canAddParticipants()) {
|
if (_peerMegagroup->canAddParticipants()) {
|
||||||
setSecondaryButton(lang(lng_profile_add_participant), SLOT(onAddMember()));
|
addButton(lang(lng_profile_add_participant), SLOT(onAddMember()));
|
||||||
} else {
|
|
||||||
clearSecondaryButton();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::setChannelButtons() {
|
void CoverWidget::setChannelButtons() {
|
||||||
if (_peerChannel->amCreator()) {
|
if (_peerChannel->amCreator()) {
|
||||||
setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||||
} else if (_peerChannel->amIn()) {
|
} else if (_peerChannel->amIn()) {
|
||||||
setPrimaryButton(lang(lng_profile_view_channel), SLOT(onViewChannel()));
|
addButton(lang(lng_profile_view_channel), SLOT(onViewChannel()));
|
||||||
} else {
|
} else {
|
||||||
setPrimaryButton(lang(lng_profile_join_channel), SLOT(onJoin()));
|
addButton(lang(lng_profile_join_channel), SLOT(onJoin()));
|
||||||
}
|
|
||||||
clearSecondaryButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoverWidget::setPrimaryButton(const QString &text, const char *slot) {
|
|
||||||
delete _primaryButton;
|
|
||||||
_primaryButton = nullptr;
|
|
||||||
if (!text.isEmpty()) {
|
|
||||||
_primaryButton = new Ui::RoundButton(this, text, st::profilePrimaryButton);
|
|
||||||
connect(_primaryButton, SIGNAL(clicked()), this, slot);
|
|
||||||
_primaryButton->show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::setSecondaryButton(const QString &text, const char *slot) {
|
void CoverWidget::clearButtons() {
|
||||||
delete _secondaryButton;
|
auto buttons = createAndSwap(_buttons);
|
||||||
_secondaryButton = nullptr;
|
for_const (auto button, buttons) {
|
||||||
|
delete button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::addButton(const QString &text, const char *slot) {
|
||||||
if (!text.isEmpty()) {
|
if (!text.isEmpty()) {
|
||||||
_secondaryButton = new Ui::RoundButton(this, text, st::profileSecondaryButton);
|
auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton;
|
||||||
connect(_secondaryButton, SIGNAL(clicked()), this, slot);
|
_buttons.push_back(new Ui::RoundButton(this, text, buttonStyle));
|
||||||
_secondaryButton->show();
|
connect(_buttons.back(), SIGNAL(clicked()), this, slot);
|
||||||
|
_buttons.back()->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +349,37 @@ void CoverWidget::onShareContact() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::onSetPhoto() {
|
void CoverWidget::onSetPhoto() {
|
||||||
|
QStringList imgExtensions(cImgExtensions());
|
||||||
|
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;All files (*.*)"));
|
||||||
|
|
||||||
|
_setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) {
|
||||||
|
if (_setPhotoFileQueryId != update.queryId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_setPhotoFileQueryId = 0;
|
||||||
|
|
||||||
|
if (update.filePaths.isEmpty() && update.remoteContent.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage img;
|
||||||
|
if (!update.remoteContent.isEmpty()) {
|
||||||
|
img = App::readImage(update.remoteContent);
|
||||||
|
} else {
|
||||||
|
img = App::readImage(update.filePaths.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
|
||||||
|
Ui::showLayer(new InformBox(lang(lng_bad_photo)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto box = new PhotoCropBox(img, _peer);
|
||||||
|
connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart()));
|
||||||
|
Ui::showLayer(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::onAddMember() {
|
void CoverWidget::onAddMember() {
|
||||||
|
@ -339,15 +387,17 @@ void CoverWidget::onAddMember() {
|
||||||
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
|
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
|
||||||
} else if (_peerChannel && _peerChannel->mgInfo) {
|
} else if (_peerChannel && _peerChannel->mgInfo) {
|
||||||
MembersAlreadyIn already;
|
MembersAlreadyIn already;
|
||||||
for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) {
|
for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
|
||||||
already.insert(*i, true);
|
already.insert(user);
|
||||||
}
|
}
|
||||||
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
|
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::onJoin() {
|
void CoverWidget::onJoin() {
|
||||||
|
if (!_peerChannel) return;
|
||||||
|
|
||||||
|
App::api()->joinChannel(_peerChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverWidget::onViewChannel() {
|
void CoverWidget::onViewChannel() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/observer.h"
|
#include "core/observer.h"
|
||||||
|
#include "ui/filedialog.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class RoundButton;
|
class RoundButton;
|
||||||
|
@ -60,6 +61,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
// Observed notifications.
|
// Observed notifications.
|
||||||
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||||
|
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
|
||||||
|
|
||||||
void refreshNameText();
|
void refreshNameText();
|
||||||
void refreshStatusText();
|
void refreshStatusText();
|
||||||
|
@ -71,14 +73,8 @@ private:
|
||||||
void setMegagroupButtons();
|
void setMegagroupButtons();
|
||||||
void setChannelButtons();
|
void setChannelButtons();
|
||||||
|
|
||||||
void setPrimaryButton(const QString &text, const char *slot);
|
void clearButtons();
|
||||||
void setSecondaryButton(const QString &text, const char *slot);
|
void addButton(const QString &text, const char *slot);
|
||||||
void clearPrimaryButton() {
|
|
||||||
setPrimaryButton(QString(), nullptr);
|
|
||||||
}
|
|
||||||
void clearSecondaryButton() {
|
|
||||||
setSecondaryButton(QString(), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void paintDivider(Painter &p);
|
void paintDivider(Painter &p);
|
||||||
|
|
||||||
|
@ -97,10 +93,11 @@ private:
|
||||||
QPoint _statusPosition;
|
QPoint _statusPosition;
|
||||||
QString _statusText;
|
QString _statusText;
|
||||||
|
|
||||||
int _dividerTop;
|
QList<Ui::RoundButton*> _buttons;
|
||||||
|
|
||||||
ChildWidget<Ui::RoundButton> _primaryButton = { nullptr };
|
int _dividerTop = 0;
|
||||||
ChildWidget<Ui::RoundButton> _secondaryButton = { nullptr };
|
|
||||||
|
FileDialog::QueryId _setPhotoFileQueryId = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) {
|
void paintEvent(QPaintEvent *e) override {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
||||||
p.fillRect(e->rect(), st::profileBg);
|
p.fillRect(e->rect(), st::profileBg);
|
||||||
|
@ -48,6 +48,11 @@ protected:
|
||||||
p.setPen(st::profileTopBarBackFg);
|
p.setPen(st::profileTopBarBackFg);
|
||||||
p.drawTextLeft(st::profileTopBarBackPosition.x(), st::profileTopBarBackPosition.y(), width(), lang(lng_menu_back));
|
p.drawTextLeft(st::profileTopBarBackPosition.x(), st::profileTopBarBackPosition.y(), width(), lang(lng_menu_back));
|
||||||
}
|
}
|
||||||
|
void onStateChanged(int oldState, ButtonStateChangeSource source) override {
|
||||||
|
if ((_state & Button::StateDown) && !(oldState & Button::StateDown)) {
|
||||||
|
emit clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
|
||||||
|
|
||||||
int notDisplayedAtBottom = height() - _visibleBottom;
|
int notDisplayedAtBottom = height() - _visibleBottom;
|
||||||
if (notDisplayedAtBottom > 0) {
|
if (notDisplayedAtBottom > 0) {
|
||||||
// decreaseAdditionalHeight(notDisplayedAtBottom);
|
decreaseAdditionalHeight(notDisplayedAtBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
//loadProfilePhotos(_visibleTop);
|
//loadProfilePhotos(_visibleTop);
|
||||||
|
@ -66,8 +66,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.fillRect(e->rect(), st::profileBg);
|
p.fillRect(e->rect(), st::profileBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::mousePressEvent(QMouseEvent *e) { // TEMP for testing
|
void InnerWidget::keyPressEvent(QKeyEvent *e) {
|
||||||
Ui::showPeerOverview(_peer, OverviewPhotos);
|
if (e->key() == Qt::Key_Escape) {
|
||||||
|
emit cancelled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int InnerWidget::resizeGetHeight(int newWidth) {
|
int InnerWidget::resizeGetHeight(int newWidth) {
|
||||||
|
|
|
@ -41,9 +41,12 @@ public:
|
||||||
// Updates the area that is visible inside the scroll container.
|
// Updates the area that is visible inside the scroll container.
|
||||||
void setVisibleTopBottom(int visibleTop, int visibleBottom);
|
void setVisibleTopBottom(int visibleTop, int visibleBottom);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void cancelled();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void mousePressEvent(QMouseEvent *e) override; // TEMP for testing
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Resizes content and counts natural widget height for the desired width.
|
// Resizes content and counts natural widget height for the desired width.
|
||||||
|
|
|
@ -48,6 +48,7 @@ Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent)
|
||||||
|
|
||||||
connect(_scroll, SIGNAL(scrolled()), _inner, SLOT(updateSelected()));
|
connect(_scroll, SIGNAL(scrolled()), _inner, SLOT(updateSelected()));
|
||||||
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||||
|
connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::updateAdaptiveLayout() {
|
void Widget::updateAdaptiveLayout() {
|
||||||
|
|
|
@ -396,8 +396,8 @@ void ProfileInner::onAddParticipant() {
|
||||||
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
|
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
|
||||||
} else if (_peerChannel && _peerChannel->mgInfo) {
|
} else if (_peerChannel && _peerChannel->mgInfo) {
|
||||||
MembersAlreadyIn already;
|
MembersAlreadyIn already;
|
||||||
for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) {
|
for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
|
||||||
already.insert(*i, true);
|
already.insert(user);
|
||||||
}
|
}
|
||||||
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
|
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,6 +229,10 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer a
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
emit App::main()->peerPhotoChanged(this);
|
emit App::main()->peerPhotoChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Notify::PeerUpdate update(this);
|
||||||
|
update.flags = Notify::PeerUpdateFlag::PhotoChanged;
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,6 +405,10 @@ void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see Loc
|
||||||
setUserpic(newPhoto);
|
setUserpic(newPhoto);
|
||||||
photoLoc = newPhotoLoc;
|
photoLoc = newPhotoLoc;
|
||||||
emit App::main()->peerPhotoChanged(this);
|
emit App::main()->peerPhotoChanged(this);
|
||||||
|
|
||||||
|
Notify::PeerUpdate update(this);
|
||||||
|
update.flags = Notify::PeerUpdateFlag::PhotoChanged;
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +447,10 @@ void ChannelData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see
|
||||||
setUserpic(newPhoto);
|
setUserpic(newPhoto);
|
||||||
photoLoc = newPhotoLoc;
|
photoLoc = newPhotoLoc;
|
||||||
if (App::main()) emit App::main()->peerPhotoChanged(this);
|
if (App::main()) emit App::main()->peerPhotoChanged(this);
|
||||||
|
|
||||||
|
Notify::PeerUpdate update(this);
|
||||||
|
update.flags = Notify::PeerUpdateFlag::PhotoChanged;
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,9 @@ public:
|
||||||
void loadUserpic(bool loadFirst = false, bool prior = true) {
|
void loadUserpic(bool loadFirst = false, bool prior = true) {
|
||||||
_userpic->load(loadFirst, prior);
|
_userpic->load(loadFirst, prior);
|
||||||
}
|
}
|
||||||
|
bool userpicLoaded() const {
|
||||||
|
return _userpic->loaded();
|
||||||
|
}
|
||||||
StorageKey userpicUniqueKey() const;
|
StorageKey userpicUniqueKey() const;
|
||||||
void saveUserpic(const QString &path) const;
|
void saveUserpic(const QString &path) const;
|
||||||
QPixmap genUserpic(int size) const;
|
QPixmap genUserpic(int size) const;
|
||||||
|
|
|
@ -234,3 +234,150 @@ QString filedialogNextFilename(const QString &name, const QString &cur, const QS
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace FileDialog {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using internal::QueryUpdateHandler;
|
||||||
|
|
||||||
|
using QueryObserversList = Notify::ObserversList<int, QueryUpdateHandler>;
|
||||||
|
NeverFreedPointer<QueryObserversList> QueryUpdateObservers;
|
||||||
|
|
||||||
|
struct Query {
|
||||||
|
enum class Type {
|
||||||
|
ReadFile,
|
||||||
|
ReadFiles,
|
||||||
|
WriteFile,
|
||||||
|
ReadFolder,
|
||||||
|
};
|
||||||
|
Query(Type type
|
||||||
|
, const QString &caption = QString()
|
||||||
|
, const QString &filter = QString()
|
||||||
|
, const QString &filePath = QString()) : id(rand_value<QueryId>())
|
||||||
|
, type(type)
|
||||||
|
, caption(caption)
|
||||||
|
, filter(filter)
|
||||||
|
, filePath(filePath) {
|
||||||
|
}
|
||||||
|
QueryId id;
|
||||||
|
Type type;
|
||||||
|
QString caption, filter, filePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
using QueryList = QList<Query>;
|
||||||
|
NeverFreedPointer<QueryList> Queries;
|
||||||
|
|
||||||
|
void StartCallback() {
|
||||||
|
QueryUpdateObservers.makeIfNull();
|
||||||
|
Queries.makeIfNull();
|
||||||
|
}
|
||||||
|
void FinishCallback() {
|
||||||
|
QueryUpdateObservers.clear();
|
||||||
|
Queries.clear();
|
||||||
|
}
|
||||||
|
void UnregisterCallback(int connectionIndex) {
|
||||||
|
t_assert(!QueryUpdateObservers.isNull());
|
||||||
|
Notify::unregisterObserver(*QueryUpdateObservers, connectionIndex);
|
||||||
|
}
|
||||||
|
Notify::ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback);
|
||||||
|
|
||||||
|
bool Started() {
|
||||||
|
return !QueryUpdateObservers.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
QueryId queryReadFile(const QString &caption, const QString &filter) {
|
||||||
|
t_assert(Started());
|
||||||
|
Queries->push_back(Query(Query::Type::ReadFile, caption, filter));
|
||||||
|
Global::RefHandleFileDialogQueue().call();
|
||||||
|
return Queries->back().id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryId queryReadFiles(const QString &caption, const QString &filter) {
|
||||||
|
t_assert(Started());
|
||||||
|
Queries->push_back(Query(Query::Type::ReadFiles, caption, filter));
|
||||||
|
Global::RefHandleFileDialogQueue().call();
|
||||||
|
return Queries->back().id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath) {
|
||||||
|
t_assert(Started());
|
||||||
|
Queries->push_back(Query(Query::Type::WriteFile, caption, filter, filePath));
|
||||||
|
Global::RefHandleFileDialogQueue().call();
|
||||||
|
return Queries->back().id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryId queryReadFolder(const QString &caption) {
|
||||||
|
t_assert(Started());
|
||||||
|
Queries->push_back(Query(Query::Type::ReadFolder, caption));
|
||||||
|
Global::RefHandleFileDialogQueue().call();
|
||||||
|
return Queries->back().id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processQuery() {
|
||||||
|
if (!Started() || !Global::started() || Queries->isEmpty()) return false;
|
||||||
|
|
||||||
|
auto query = Queries->front();
|
||||||
|
Queries->pop_front();
|
||||||
|
|
||||||
|
QueryUpdate update(query.id);
|
||||||
|
|
||||||
|
switch (query.type) {
|
||||||
|
case Query::Type::ReadFile: {
|
||||||
|
QString file;
|
||||||
|
QByteArray remoteContent;
|
||||||
|
if (filedialogGetOpenFile(file, remoteContent, query.caption, query.filter)) {
|
||||||
|
if (!file.isEmpty()) {
|
||||||
|
update.filePaths.push_back(file);
|
||||||
|
}
|
||||||
|
update.remoteContent = remoteContent;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Query::Type::ReadFiles: {
|
||||||
|
QStringList files;
|
||||||
|
QByteArray remoteContent;
|
||||||
|
if (filedialogGetOpenFiles(files, remoteContent, query.caption, query.filter)) {
|
||||||
|
update.filePaths = files;
|
||||||
|
update.remoteContent = remoteContent;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Query::Type::WriteFile: {
|
||||||
|
QString file;
|
||||||
|
if (filedialogGetSaveFile(file, query.caption, query.filter, query.filePath)) {
|
||||||
|
if (!file.isEmpty()) {
|
||||||
|
update.filePaths.push_back(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Query::Type::ReadFolder: {
|
||||||
|
QString folder;
|
||||||
|
if (filedialogGetDir(folder, query.caption)) {
|
||||||
|
if (!folder.isEmpty()) {
|
||||||
|
update.filePaths.push_back(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No one know what happened during filedialogGet*() call in the event loop.
|
||||||
|
if (!Started() || !Global::started()) return false;
|
||||||
|
|
||||||
|
Notify::notifyObservers(*QueryUpdateObservers, Notify::UniversalFlag, update);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler) {
|
||||||
|
t_assert(Started());
|
||||||
|
auto connectionId = Notify::registerObserver(creator.event(), *QueryUpdateObservers
|
||||||
|
, Notify::UniversalFlag, std_::forward<QueryUpdateHandler>(handler));
|
||||||
|
return connectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace FileDialog
|
||||||
|
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
void filedialogInit();
|
void filedialogInit();
|
||||||
bool filedialogGetOpenFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter);
|
bool filedialogGetOpenFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter);
|
||||||
bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QString &caption, const QString &filter);
|
bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QString &caption, const QString &filter);
|
||||||
|
@ -28,3 +30,38 @@ bool filedialogGetDir(QString &dir, const QString &caption);
|
||||||
|
|
||||||
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false);
|
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false);
|
||||||
QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString());
|
QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString());
|
||||||
|
|
||||||
|
namespace FileDialog {
|
||||||
|
|
||||||
|
using QueryId = uint64;
|
||||||
|
struct QueryUpdate {
|
||||||
|
QueryUpdate(QueryId id) : queryId(id) {
|
||||||
|
}
|
||||||
|
QueryId queryId;
|
||||||
|
QStringList filePaths;
|
||||||
|
QByteArray remoteContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryId queryReadFile(const QString &caption, const QString &filter);
|
||||||
|
QueryId queryReadFiles(const QString &caption, const QString &filter);
|
||||||
|
QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath);
|
||||||
|
QueryId queryReadFolder(const QString &caption);
|
||||||
|
|
||||||
|
// Returns false if no need to call it anymore right now.
|
||||||
|
// NB! This function enters an event loop.
|
||||||
|
bool processQuery();
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
using QueryUpdateHandler = Function<void, const QueryUpdate&>;
|
||||||
|
Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
void registerObserver(ObserverType *observer, void (ObserverType::*handler)(const QueryUpdate &)) {
|
||||||
|
auto connection = internal::plainRegisterObserver(func(observer, handler));
|
||||||
|
Notify::observerRegistered(observer, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileDialog
|
||||||
|
|
|
@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtGui/QPixmap>
|
#include "mtproto/file_download.h"
|
||||||
|
|
||||||
QImage imageBlur(QImage img);
|
QImage imageBlur(QImage img);
|
||||||
void imageRound(QImage &img);
|
void imageRound(QImage &img);
|
||||||
|
|
Loading…
Reference in New Issue