diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e555ccf916..751757c636 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -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) { switch (msgs.type()) { 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) { _stickerSetRequests.remove(setId); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 606ef7f14f..4e223d2a59 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -28,7 +28,7 @@ public: ApiWrap(QObject *parent); void init(); - typedef SharedCallback RequestMessageDataCallback; + using RequestMessageDataCallback = SharedCallback; void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr callback); void requestFullPeer(PeerData *peer); @@ -50,6 +50,9 @@ public: void scheduleStickerSetRequest(uint64 setId, uint64 access); void requestStickerSets(); + void joinChannel(ChannelData *channel); + void leaveChannel(ChannelData *channel); + ~ApiWrap(); signals: @@ -65,6 +68,8 @@ public slots: private: + void updatesReceived(const MTPUpdates &updates); + void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req); struct MessageDataRequest { MessageDataRequest() : req(0) { @@ -120,4 +125,9 @@ private: void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result); bool gotStickerSetFail(uint64 setId, const RPCError &error); + QMap _channelAmInRequests; + void channelAmInUpdated(ChannelData *channel); + void channelAmInDone(ChannelData *channel, const MTPUpdates &updates); + bool channelAmInFail(ChannelData *channel, const RPCError &error); + }; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index bdb4ad3dd4..093b8fba78 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -369,7 +369,7 @@ namespace { UserData *result = nullptr; for_const (auto &user, users.c_vector().v) { UserData *data = nullptr; - bool wasContact = false, canShareContact = false, minimal = false; + bool wasContact = false, minimal = false; const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty(); Notify::PeerUpdate update; @@ -548,12 +548,13 @@ namespace { switch (chat.type()) { case mtpc_chat: { - const auto &d(chat.c_chat()); + auto &d(chat.c_chat()); 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->setPhoto(d.vphoto); cdata->date = d.vdate.v; @@ -604,14 +605,18 @@ namespace { cdata->version = d.vversion.v; cdata->invalidateParticipants(); } + if (canEdit != cdata->canEdit()) { + update.flags |= UpdateFlag::ChatCanEdit; + } } break; case mtpc_chatForbidden: { - const auto &d(chat.c_chatForbidden()); + auto &d(chat.c_chatForbidden()); 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->setPhoto(MTP_chatPhotoEmpty()); cdata->date = 0; @@ -619,27 +624,32 @@ namespace { cdata->invalidateParticipants(); cdata->flags = 0; cdata->isForbidden = true; + if (canEdit != cdata->canEdit()) { + update.flags |= UpdateFlag::ChatCanEdit; + } } break; 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(); if (minimal) { - data = App::channelLoaded(peer); + data = App::channelLoaded(peerId); if (!data) { continue; // minimal is not loaded, need to make getDifference } } 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)); } - ChannelData *cdata = data->asChannel(); + auto cdata = data->asChannel(); auto wasInChannel = cdata->amIn(); + auto canEditPhoto = cdata->canEditPhoto(); + auto canAddMembers = cdata->canAddParticipants(); 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); } else { cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); @@ -662,19 +672,27 @@ namespace { cdata->flagsUpdated(); cdata->setPhoto(d.vphoto); - if (wasInChannel != cdata->amIn() && !cdata->isMegagroup()) { + if (wasInChannel != cdata->amIn()) { update.flags |= UpdateFlag::ChannelAmIn; } + if (canEditPhoto != cdata->canEditPhoto()) { + update.flags |= UpdateFlag::ChannelCanEditPhoto; + } + if (canAddMembers != cdata->canAddParticipants()) { + update.flags |= UpdateFlag::ChannelCanAddMembers; + } } break; case mtpc_channelForbidden: { - const auto &d(chat.c_channelForbidden()); + auto &d(chat.c_channelForbidden()); - PeerId peer(peerFromChannel(d.vid.v)); - data = App::channel(peer); + auto peerId = peerFromChannel(d.vid.v); + data = App::channel(peerId); data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash); - ChannelData *cdata = data->asChannel(); + auto cdata = data->asChannel(); auto wasInChannel = cdata->amIn(); + auto canEditPhoto = cdata->canEditPhoto(); + auto canAddMembers = cdata->canAddParticipants(); cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); @@ -686,9 +704,15 @@ namespace { cdata->count = 0; cdata->isForbidden = true; - if (wasInChannel != cdata->amIn() && !cdata->isMegagroup()) { + if (wasInChannel != cdata->amIn()) { update.flags |= UpdateFlag::ChannelAmIn; } + if (canEditPhoto != cdata->canEditPhoto()) { + update.flags |= UpdateFlag::ChannelCanEditPhoto; + } + if (canAddMembers != cdata->canAddParticipants()) { + update.flags |= UpdateFlag::ChannelCanAddMembers; + } } break; } if (!data) continue; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index b73ecca3af..4ff6daddff 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -27,10 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "lang.h" #include "boxes/confirmbox.h" +#include "ui/filedialog.h" #include "langloaderplain.h" #include "localstorage.h" #include "autoupdater.h" #include "core/observer.h" +#include "observer_peer.h" namespace { void mtpStateChanged(int32 dc, int32 state) { @@ -817,6 +819,7 @@ void AppClass::doMtpUnpause() { void AppClass::selfPhotoCleared(const MTPUserProfilePhoto &result) { if (!App::self()) return; App::self()->setPhoto(result); + Notify::peerUpdatedSendDelayed(); 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() { uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout; for (QMap::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) { diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h index f894102fe8..953b503e50 100644 --- a/Telegram/SourceFiles/application.h +++ b/Telegram/SourceFiles/application.h @@ -203,6 +203,7 @@ public slots: void call_handleHistoryUpdate(); void call_handleUnreadCounterUpdate(); + void call_handleFileDialogQueue(); private: diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index b79f89819b..43dfe7a93b 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -2003,11 +2003,11 @@ MembersFilter MembersInner::filter() const { return _filter; } -QMap MembersInner::already() const { +MembersAlreadyIn MembersInner::already() const { MembersAlreadyIn result; - for (int32 i = 0, l = _rows.size(); i < l; ++i) { - if (_rows.at(i)->isUser()) { - result.insert(_rows.at(i)->asUser(), true); + for_const (auto peer, _rows) { + if (peer->isUser()) { + result.insert(peer->asUser()); } } return result; diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index e1bb53673a..d40ce59240 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -31,7 +31,7 @@ enum MembersFilter { MembersFilterRecent, MembersFilterAdmins, }; -typedef QMap MembersAlreadyIn; +using MembersAlreadyIn = OrderedSet; QString cantInviteError(); @@ -318,7 +318,7 @@ public: } void clearSel(); - QMap already() const; + MembersAlreadyIn already() const; ~MembersInner(); diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index 4907c2855c..c5240c1b9d 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -33,6 +33,13 @@ T *getPointerAndReset(T *&ptr) { return result; } +template +T createAndSwap(T &value) { + T result; + std::swap(result, value); + return result; +} + struct NullType { }; diff --git a/Telegram/SourceFiles/core/observer.cpp b/Telegram/SourceFiles/core/observer.cpp index 607b28911d..65a9ffd7c5 100644 --- a/Telegram/SourceFiles/core/observer.cpp +++ b/Telegram/SourceFiles/core/observer.cpp @@ -30,6 +30,8 @@ NeverFreedPointer StartCallbacks; NeverFreedPointer FinishCallbacks; UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/; +ObservedEvent LastRegisteredEvent/* = 0*/; + } // namespace void startObservers() { @@ -50,17 +52,18 @@ void finishObservers() { FinishCallbacks.clear(); } -ObservedEventRegistrator::ObservedEventRegistrator(ObservedEvent event -, StartObservedEventCallback startCallback +ObservedEventRegistrator::ObservedEventRegistrator(StartObservedEventCallback startCallback , FinishObservedEventCallback finishCallback , UnregisterObserverCallback unregisterCallback) { + _event = LastRegisteredEvent++; + StartCallbacks.makeIfNull(); StartCallbacks->push_back(startCallback); FinishCallbacks.makeIfNull(); FinishCallbacks->push_back(finishCallback); - UnregisterCallbacks[event] = unregisterCallback; + UnregisterCallbacks[_event] = unregisterCallback; } // Observer base interface. diff --git a/Telegram/SourceFiles/core/observer.h b/Telegram/SourceFiles/core/observer.h index 8ed722dbca..9ffa55f76d 100644 --- a/Telegram/SourceFiles/core/observer.h +++ b/Telegram/SourceFiles/core/observer.h @@ -42,10 +42,16 @@ using FinishObservedEventCallback = void(*)(); // unregisterCallback will be used to destroy connections. class ObservedEventRegistrator { public: - ObservedEventRegistrator(ObservedEvent event - , StartObservedEventCallback startCallback - , FinishObservedEventCallback finishCallback - , UnregisterObserverCallback unregisterCallback); + ObservedEventRegistrator(StartObservedEventCallback startCallback, + FinishObservedEventCallback finishCallback, + UnregisterObserverCallback unregisterCallback); + + inline ObservedEvent event() const { + return _event; + } + +private: + ObservedEvent _event; }; @@ -88,8 +94,12 @@ struct ObserversList { QVector 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 -int registerObserver(ObserversList &list, Flags flags, Handler &&handler) { +ConnectionId registerObserver(ObservedEvent event, ObserversList &list, Flags flags, Handler &&handler) { while (!list.freeIndices.isEmpty()) { auto freeIndex = list.freeIndices.back(); list.freeIndices.pop_back(); @@ -100,7 +110,8 @@ int registerObserver(ObserversList &list, Flags flags, Handler & } } list.entries.push_back({ flags, std_::move(handler) }); - return list.entries.size() - 1; + int connectionIndex = list.entries.size() - 1; + return (static_cast(event) << 24) | static_cast(connectionIndex + 1); } template @@ -131,17 +142,26 @@ namespace internal { template struct ObserverRegisteredGeneric { - static void call(ObserverType *observer, ConnectionId connection) { + static inline void call(ObserverType *observer, ConnectionId connection) { observer->observerRegistered(connection); } }; -template +template struct ObserverRegisteredGeneric { - static void call(ObserverType *observer, ConnectionId connection) { + static inline void call(ObserverType *observer, ConnectionId connection) { observerRegisteredDefault(observer, connection); } }; } // namespace internal + +template +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::value>; + ObserverRegistered::call(observer, connection); +} + } // namespace Notify diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index b1aebc849e..1cc382d30d 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -519,6 +519,7 @@ struct Data { uint64 LaunchId = 0; SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" }; SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" }; + SingleDelayedCall HandleFileDialogQueue = { App::app(), "call_handleFileDialogQueue" }; Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout; bool AdaptiveForWide = true; @@ -582,6 +583,7 @@ void finish() { DefineReadOnlyVar(Global, uint64, LaunchId); DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate); DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate); +DefineRefVar(Global, SingleDelayedCall, HandleFileDialogQueue); DefineVar(Global, Adaptive::Layout, AdaptiveLayout); DefineVar(Global, bool, AdaptiveForWide); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 0966f9d9b1..c6fae8f470 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -204,6 +204,7 @@ void finish(); DeclareReadOnlyVar(uint64, LaunchId); DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate); DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate); +DeclareRefVar(SingleDelayedCall, HandleFileDialogQueue); DeclareVar(Adaptive::Layout, AdaptiveLayout); DeclareVar(bool, AdaptiveForWide); diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index dfd5f22ad0..6634fb8126 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mtproto/core_types.h" #include "mtproto/session.h" -#include "mtproto/file_download.h" namespace MTP { diff --git a/Telegram/SourceFiles/mtproto/file_download.cpp b/Telegram/SourceFiles/mtproto/file_download.cpp index 73d61b1318..71353c24a2 100644 --- a/Telegram/SourceFiles/mtproto/file_download.cpp +++ b/Telegram/SourceFiles/mtproto/file_download.cpp @@ -208,6 +208,7 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray & } emit App::wnd()->imageLoaded(); emit progress(this); + FileDownload::internal::notifyImageLoaded(); 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))); } emit progress(this); + if (_complete) { + FileDownload::internal::notifyImageLoaded(); + } loadNext(); } @@ -678,6 +682,7 @@ void webFileLoader::onFinished(const QByteArray &data) { Local::writeWebFile(_url, _data); } emit progress(this); + FileDownload::internal::notifyImageLoaded(); loadNext(); } @@ -1089,3 +1094,45 @@ namespace MTP { ++GlobalPriority; } } + +namespace FileDownload { +namespace { + +using internal::ImageLoadedHandler; + +using ImageLoadedObserversList = Notify::ObserversList; +NeverFreedPointer 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(handler)); + return connectionId; +} + +void notifyImageLoaded() { + Notify::notifyObservers(*ImageLoadedObservers, Notify::UniversalFlag); +} + +} // namespace internal +} \ No newline at end of file diff --git a/Telegram/SourceFiles/mtproto/file_download.h b/Telegram/SourceFiles/mtproto/file_download.h index 35c6a095c9..f8a05d7ea6 100644 --- a/Telegram/SourceFiles/mtproto/file_download.h +++ b/Telegram/SourceFiles/mtproto/file_download.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "core/observer.h" + namespace MTP { void clearLoaderPriorities(); } @@ -391,3 +393,21 @@ static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation; +Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler); + +void notifyImageLoaded(); + +} // namespace internal + +template +void registerImageLoadedObserver(ObserverType *observer, void (ObserverType::*handler)()) { + auto connection = internal::plainRegisterImageLoadedObserver(func(observer, handler)); + Notify::observerRegistered(observer, connection); +} + +} // namespace FileDownload diff --git a/Telegram/SourceFiles/observer_peer.cpp b/Telegram/SourceFiles/observer_peer.cpp index c77ec90bf8..d1cbbde794 100644 --- a/Telegram/SourceFiles/observer_peer.cpp +++ b/Telegram/SourceFiles/observer_peer.cpp @@ -32,7 +32,6 @@ namespace Notify { namespace internal { namespace { -constexpr ObservedEvent PeerUpdateEvent = 0x01; using PeerObserversList = ObserversList; NeverFreedPointer PeerUpdateObservers; @@ -55,7 +54,7 @@ void UnregisterCallback(int connectionIndex) { t_assert(!PeerUpdateObservers.isNull()); unregisterObserver(*PeerUpdateObservers, connectionIndex); } -ObservedEventRegistrator creator(PeerUpdateEvent, StartCallback, FinishCallback, UnregisterCallback); +ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback); bool Started() { return !PeerUpdateObservers.isNull(); @@ -65,9 +64,9 @@ bool Started() { ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) { t_assert(Started()); - auto connectionId = registerObserver(*PeerUpdateObservers, events, std_::forward(handler)); - t_assert(connectionId >= 0 && connectionId < 0x01000000); - return (static_cast(PeerUpdateEvent) << 24) | static_cast(connectionId + 1); + auto connectionId = registerObserver(creator.event(), *PeerUpdateObservers + , events, std_::forward(handler)); + return connectionId; } void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) { @@ -116,10 +115,8 @@ void peerUpdatedSendDelayed() { if (internal::SmallUpdates->isEmpty()) return; - internal::SmallUpdatesList smallList; - internal::AllUpdatesList allList; - std::swap(smallList, *internal::SmallUpdates); - std::swap(allList, *internal::AllUpdates); + auto smallList = createAndSwap(*internal::SmallUpdates); + auto allList = createAndSwap(*internal::AllUpdates); for_const (auto &update, smallList) { notifyObservers(*internal::PeerUpdateObservers, update.flags, update); } diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index a490f0b110..18a425bcf7 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -30,16 +30,17 @@ namespace Notify { // 0xFFFF0000U for specific peer updates (valid for user / chat / channel). enum class PeerUpdateFlag { - NameChanged = 0x00000001U, - UsernameChanged = 0x00000002U, + NameChanged = 0x00000001U, + UsernameChanged = 0x00000002U, + PhotoChanged = 0x00000004U, - UserCanShareContact = 0x00010000U, + UserCanShareContact = 0x00010000U, - ChatCanEdit = 0x00010000U, + ChatCanEdit = 0x00010000U, - ChannelAmIn = 0x00010000U, - MegagroupCanEditPhoto = 0x00020000U, - MegagroupCanAddMembers = 0x00040000U, + ChannelAmIn = 0x00010000U, + ChannelCanEditPhoto = 0x00020000U, + ChannelCanAddMembers = 0x00040000U, }; Q_DECLARE_FLAGS(PeerUpdateFlags, PeerUpdateFlag); Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdateFlags); @@ -68,11 +69,7 @@ ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler template void registerPeerObserver(PeerUpdateFlags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) { auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler)); - - // 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::value>; - ObserverRegistered::call(observer, connection); + observerRegistered(observer, connection); } } // namespace Notify diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index 46620a7e59..e9bf3666d2 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -23,8 +23,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" #include "ui/buttons/round_button.h" +#include "ui/filedialog.h" #include "observer_peer.h" +#include "boxes/confirmbox.h" #include "boxes/contactsbox.h" +#include "boxes/photocropbox.h" #include "lang.h" #include "apiwrap.h" #include "mainwidget.h" @@ -62,31 +65,61 @@ private: const Notify::PeerUpdateFlags ButtonsUpdateFlags = Notify::PeerUpdateFlag::UserCanShareContact | Notify::PeerUpdateFlag::ChatCanEdit - | Notify::PeerUpdateFlag::MegagroupCanEditPhoto - | Notify::PeerUpdateFlag::MegagroupCanAddMembers + | Notify::PeerUpdateFlag::ChannelCanEditPhoto + | Notify::PeerUpdateFlag::ChannelCanAddMembers | Notify::PeerUpdateFlag::ChannelAmIn; } // namespace -class PhotoButton final : public Button { +class PhotoButton final : public Button, public Notify::Observer { public: PhotoButton(QWidget *parent, PeerData *peer) : Button(parent), _peer(peer) { resize(st::profilePhotoSize, st::profilePhotoSize); - } - void photoUpdated() { - bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId); - setCursor(hasPhoto ? style::cur_pointer : style::cur_default); + + processNewPeerPhoto(); + + Notify::registerPeerObserver(Notify::PeerUpdateFlag::PhotoChanged, this, &PhotoButton::notifyPeerUpdated); + FileDownload::registerImageLoadedObserver(this, &PhotoButton::notifyImageLoaded); } protected: void paintEvent(QPaintEvent *e) { Painter p(this); - - _peer->paintUserpic(p, st::profilePhotoSize, 0, 0); + p.drawPixmap(0, 0, _userpic); } 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; + bool _waiting = false; + QPixmap _userpic; }; @@ -101,8 +134,8 @@ CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent) auto observeEvents = ButtonsUpdateFlags | Notify::PeerUpdateFlag::NameChanged; Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated); + FileDialog::registerObserver(this, &CoverWidget::notifyFileQueryUpdated); - _photoButton->photoUpdated(); connect(_photoButton, SIGNAL(clicked()), this, SLOT(onPhotoShow())); refreshNameText(); @@ -132,12 +165,9 @@ void CoverWidget::resizeToWidth(int newWidth) { _statusPosition = QPoint(infoLeft + st::profileStatusLeft, _photoButton->y() + st::profileStatusTop); int buttonLeft = st::profilePhotoLeft + _photoButton->width() + st::profileButtonLeft; - if (_primaryButton) { - _primaryButton->moveToLeft(buttonLeft, st::profileButtonTop); - buttonLeft += _primaryButton->width() + st::profileButtonSkip; - } - if (_secondaryButton) { - _secondaryButton->moveToLeft(buttonLeft, st::profileButtonTop); + for_const (auto button, _buttons) { + button->moveToLeft(buttonLeft, st::profileButtonTop); + buttonLeft += button->width() + st::profileButtonSkip; } newHeight += st::profilePhotoSize; @@ -178,13 +208,14 @@ void CoverWidget::paintDivider(Painter &p) { } void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { - if (update.peer == _peer) { - if ((update.flags & ButtonsUpdateFlags) != 0) { - refreshButtons(); - } - if (update.flags & Notify::PeerUpdateFlag::NameChanged) { - refreshNameText(); - } + if (update.peer != _peer) { + return; + } + if ((update.flags & ButtonsUpdateFlags) != 0) { + refreshButtons(); + } + if (update.flags & Notify::PeerUpdateFlag::NameChanged) { + refreshNameText(); } } @@ -247,6 +278,7 @@ bool CoverWidget::isUsingMegagroupOnlineCount() const { } void CoverWidget::refreshButtons() { + clearButtons(); if (_peerUser) { setUserButtons(); } else if (_peerChat) { @@ -260,65 +292,51 @@ void CoverWidget::refreshButtons() { } void CoverWidget::setUserButtons() { - setPrimaryButton(lang(lng_profile_send_message), SLOT(onSendMessage())); + addButton(lang(lng_profile_send_message), SLOT(onSendMessage())); if (_peerUser->canShareThisContact()) { - setSecondaryButton(lang(lng_profile_share_contact), SLOT(onShareContact())); - } else { - clearSecondaryButton(); + addButton(lang(lng_profile_share_contact), SLOT(onShareContact())); } } void CoverWidget::setChatButtons() { if (_peerChat->canEdit()) { - setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); - setSecondaryButton(lang(lng_profile_add_participant), SLOT(onAddMember())); - } else { - clearPrimaryButton(); - clearSecondaryButton(); + addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); + addButton(lang(lng_profile_add_participant), SLOT(onAddMember())); } } void CoverWidget::setMegagroupButtons() { if (_peerMegagroup->canEditPhoto()) { - setPrimaryButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); - } else { - clearPrimaryButton(); + addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto())); } if (_peerMegagroup->canAddParticipants()) { - setSecondaryButton(lang(lng_profile_add_participant), SLOT(onAddMember())); - } else { - clearSecondaryButton(); + addButton(lang(lng_profile_add_participant), SLOT(onAddMember())); } } void CoverWidget::setChannelButtons() { 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()) { - setPrimaryButton(lang(lng_profile_view_channel), SLOT(onViewChannel())); + addButton(lang(lng_profile_view_channel), SLOT(onViewChannel())); } else { - setPrimaryButton(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(); + addButton(lang(lng_profile_join_channel), SLOT(onJoin())); } } -void CoverWidget::setSecondaryButton(const QString &text, const char *slot) { - delete _secondaryButton; - _secondaryButton = nullptr; +void CoverWidget::clearButtons() { + auto buttons = createAndSwap(_buttons); + for_const (auto button, buttons) { + delete button; + } +} + +void CoverWidget::addButton(const QString &text, const char *slot) { if (!text.isEmpty()) { - _secondaryButton = new Ui::RoundButton(this, text, st::profileSecondaryButton); - connect(_secondaryButton, SIGNAL(clicked()), this, slot); - _secondaryButton->show(); + auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton; + _buttons.push_back(new Ui::RoundButton(this, text, buttonStyle)); + connect(_buttons.back(), SIGNAL(clicked()), this, slot); + _buttons.back()->show(); } } @@ -331,7 +349,37 @@ void CoverWidget::onShareContact() { } 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() { @@ -339,15 +387,17 @@ void CoverWidget::onAddMember() { Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); } else if (_peerChannel && _peerChannel->mgInfo) { MembersAlreadyIn already; - for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { - already.insert(*i, true); + for_const (auto user, _peerChannel->mgInfo->lastParticipants) { + already.insert(user); } Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); } } void CoverWidget::onJoin() { + if (!_peerChannel) return; + App::api()->joinChannel(_peerChannel); } void CoverWidget::onViewChannel() { diff --git a/Telegram/SourceFiles/profile/profile_cover.h b/Telegram/SourceFiles/profile/profile_cover.h index 96b7d668f8..da3a5dcd53 100644 --- a/Telegram/SourceFiles/profile/profile_cover.h +++ b/Telegram/SourceFiles/profile/profile_cover.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "core/observer.h" +#include "ui/filedialog.h" namespace Ui { class RoundButton; @@ -60,6 +61,7 @@ protected: private: // Observed notifications. void notifyPeerUpdated(const Notify::PeerUpdate &update); + void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); void refreshNameText(); void refreshStatusText(); @@ -71,14 +73,8 @@ private: void setMegagroupButtons(); void setChannelButtons(); - void setPrimaryButton(const QString &text, const char *slot); - void setSecondaryButton(const QString &text, const char *slot); - void clearPrimaryButton() { - setPrimaryButton(QString(), nullptr); - } - void clearSecondaryButton() { - setSecondaryButton(QString(), nullptr); - } + void clearButtons(); + void addButton(const QString &text, const char *slot); void paintDivider(Painter &p); @@ -97,10 +93,11 @@ private: QPoint _statusPosition; QString _statusText; - int _dividerTop; + QList _buttons; - ChildWidget _primaryButton = { nullptr }; - ChildWidget _secondaryButton = { nullptr }; + int _dividerTop = 0; + + FileDialog::QueryId _setPhotoFileQueryId = 0; }; diff --git a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp index a640633c84..9c3b30f64a 100644 --- a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp +++ b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp @@ -38,7 +38,7 @@ public: } protected: - void paintEvent(QPaintEvent *e) { + void paintEvent(QPaintEvent *e) override { Painter p(this); p.fillRect(e->rect(), st::profileBg); @@ -48,6 +48,11 @@ protected: p.setPen(st::profileTopBarBackFg); 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: diff --git a/Telegram/SourceFiles/profile/profile_inner_widget.cpp b/Telegram/SourceFiles/profile/profile_inner_widget.cpp index 408ecf18c6..d606c74356 100644 --- a/Telegram/SourceFiles/profile/profile_inner_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_inner_widget.cpp @@ -45,7 +45,7 @@ void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) { int notDisplayedAtBottom = height() - _visibleBottom; if (notDisplayedAtBottom > 0) { -// decreaseAdditionalHeight(notDisplayedAtBottom); + decreaseAdditionalHeight(notDisplayedAtBottom); } //loadProfilePhotos(_visibleTop); @@ -66,8 +66,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) { p.fillRect(e->rect(), st::profileBg); } -void InnerWidget::mousePressEvent(QMouseEvent *e) { // TEMP for testing - Ui::showPeerOverview(_peer, OverviewPhotos); +void InnerWidget::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Escape) { + emit cancelled(); + } } int InnerWidget::resizeGetHeight(int newWidth) { diff --git a/Telegram/SourceFiles/profile/profile_inner_widget.h b/Telegram/SourceFiles/profile/profile_inner_widget.h index 66540915f6..6a1ad15002 100644 --- a/Telegram/SourceFiles/profile/profile_inner_widget.h +++ b/Telegram/SourceFiles/profile/profile_inner_widget.h @@ -41,9 +41,12 @@ public: // Updates the area that is visible inside the scroll container. void setVisibleTopBottom(int visibleTop, int visibleBottom); +signals: + void cancelled(); + protected: void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; // TEMP for testing + void keyPressEvent(QKeyEvent *e) override; private: // Resizes content and counts natural widget height for the desired width. diff --git a/Telegram/SourceFiles/profile/profile_widget.cpp b/Telegram/SourceFiles/profile/profile_widget.cpp index d943a3eaf0..ca36721855 100644 --- a/Telegram/SourceFiles/profile/profile_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_widget.cpp @@ -48,6 +48,7 @@ Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent) connect(_scroll, SIGNAL(scrolled()), _inner, SLOT(updateSelected())); connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack())); } void Widget::updateAdaptiveLayout() { diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index b6d5e993f9..6768a2b363 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -396,8 +396,8 @@ void ProfileInner::onAddParticipant() { Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); } else if (_peerChannel && _peerChannel->mgInfo) { MembersAlreadyIn already; - for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { - already.insert(*i, true); + for_const (auto user, _peerChannel->mgInfo->lastParticipants) { + already.insert(user); } Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 25170670ac..a3e3f72bd4 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -229,6 +229,10 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer a if (App::main()) { 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); photoLoc = newPhotoLoc; 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); photoLoc = newPhotoLoc; if (App::main()) emit App::main()->peerPhotoChanged(this); + + Notify::PeerUpdate update(this); + update.flags = Notify::PeerUpdateFlag::PhotoChanged; + Notify::peerUpdatedDelayed(update); } } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 2b52bb830a..99698b27d1 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -309,6 +309,9 @@ public: void loadUserpic(bool loadFirst = false, bool prior = true) { _userpic->load(loadFirst, prior); } + bool userpicLoaded() const { + return _userpic->loaded(); + } StorageKey userpicUniqueKey() const; void saveUserpic(const QString &path) const; QPixmap genUserpic(int size) const; diff --git a/Telegram/SourceFiles/ui/filedialog.cpp b/Telegram/SourceFiles/ui/filedialog.cpp index f3d9cc8291..aaf02498c3 100644 --- a/Telegram/SourceFiles/ui/filedialog.cpp +++ b/Telegram/SourceFiles/ui/filedialog.cpp @@ -234,3 +234,150 @@ QString filedialogNextFilename(const QString &name, const QString &cur, const QS } return result; } + +namespace FileDialog { +namespace { + +using internal::QueryUpdateHandler; + +using QueryObserversList = Notify::ObserversList; +NeverFreedPointer 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()) + , type(type) + , caption(caption) + , filter(filter) + , filePath(filePath) { + } + QueryId id; + Type type; + QString caption, filter, filePath; +}; + +using QueryList = QList; +NeverFreedPointer 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(handler)); + return connectionId; +} + +} // namespace internal +} // namespace FileDialog diff --git a/Telegram/SourceFiles/ui/filedialog.h b/Telegram/SourceFiles/ui/filedialog.h index 33f3f13918..9a8317e37b 100644 --- a/Telegram/SourceFiles/ui/filedialog.h +++ b/Telegram/SourceFiles/ui/filedialog.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "core/observer.h" + void filedialogInit(); bool filedialogGetOpenFiles(QStringList &files, 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 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; +Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler); + +} // namespace internal + +template +void registerObserver(ObserverType *observer, void (ObserverType::*handler)(const QueryUpdate &)) { + auto connection = internal::plainRegisterObserver(func(observer, handler)); + Notify::observerRegistered(observer, connection); +} + +} // namespace FileDialog diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index 036c48f338..3cc8f1f3b3 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include +#include "mtproto/file_download.h" QImage imageBlur(QImage img); void imageRound(QImage &img);