/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once #include "boxes/abstractbox.h" #include "core/single_timer.h" #include "ui/effects/round_checkbox.h" #include "boxes/members_box.h" namespace Dialogs { class Row; class IndexedList; } // namespace Dialogs namespace Ui { class RoundButton; class LinkButton; class Checkbox; class MultiSelect; template class WidgetSlideWrap; } // namespace Ui QString cantInviteError(); inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) { return [peer](Painter &p, int x, int y, int outerWidth, int size) { peer->paintUserpicLeft(p, size, x, y, outerWidth); }; } class ContactsBox : public ItemListBox, public RPCSender { Q_OBJECT public: ContactsBox(); ContactsBox(const QString &name, const QImage &photo); // group creation ContactsBox(ChannelData *channel); // channel setup ContactsBox(ChannelData *channel, MembersFilter filter, const MembersAlreadyIn &already); ContactsBox(ChatData *chat, MembersFilter filter); ContactsBox(UserData *bot); signals: void adminAdded(); private slots: void onScroll(); void onInvite(); void onCreate(); void onSaveAdmins(); void onSubmit(); bool onSearchByUsername(bool searchCache = false); void onNeedSearchByUsername(); protected: void keyPressEvent(QKeyEvent *e) override; void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; void closePressed() override; void doSetInnerFocus() override; private: void init(); int getTopScrollSkip() const; void updateScrollSkips(); void onFilterUpdate(const QString &filter); void onPeerSelectedChanged(PeerData *peer, bool checked); void addPeerToMultiSelect(PeerData *peer, bool skipAnimation = false); class Inner; ChildWidget _inner; ChildWidget> _select; ChildWidget _add = { nullptr }; ChildWidget _next; ChildWidget _cancel; MembersFilter _membersFilter; ChildWidget _topShadow; ChildWidget _bottomShadow = { nullptr }; void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req); bool peopleFailed(const RPCError &error, mtpRequestId req); QTimer _searchTimer; QString _peopleQuery; bool _peopleFull; mtpRequestId _peopleRequest; typedef QMap PeopleCache; PeopleCache _peopleCache; typedef QMap PeopleQueries; PeopleQueries _peopleQueries; mtpRequestId _saveRequestId = 0; // saving admins void saveAdminsDone(const MTPUpdates &result); void saveSelectedAdmins(); void getAdminsDone(const MTPmessages_ChatFull &result); void setAdminDone(UserData *user, const MTPBool &result); void removeAdminDone(UserData *user, const MTPBool &result); bool saveAdminsFail(const RPCError &error); bool editAdminFail(const RPCError &error); // group creation QString _creationName; QImage _creationPhoto; void creationDone(const MTPUpdates &updates); bool creationFail(const RPCError &e); }; // This class is hold in header because it requires Qt preprocessing. class ContactsBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: Inner(QWidget *parent, CreatingGroupType creating = CreatingGroupNone); Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already); Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter); Inner(QWidget *parent, UserData *bot); void setPeerSelectedChangedCallback(base::lambda &&callback); void peerUnselected(PeerData *peer); void updateFilter(QString filter = QString()); void updateSelection(); void selectSkip(int32 dir); void selectSkipPage(int32 h, int32 dir); QVector selected(); QVector selectedInputs(); bool allAdmins() const; void setAllAdminsChangedCallback(base::lambda &&allAdminsChangedCallback) { _allAdminsChangedCallback = std_::move(allAdminsChangedCallback); } void loadProfilePhotos(int32 yFrom); void chooseParticipant(); void peopleReceived(const QString &query, const QVector &people); void refresh(); ChatData *chat() const; ChannelData *channel() const; MembersFilter membersFilter() const; UserData *bot() const; CreatingGroupType creating() const; bool sharingBotGame() const; int32 selectedCount() const; bool hasAlreadyMembersInChannel() const { return !_already.isEmpty(); } void saving(bool flag); ~Inner(); signals: void mustScrollTo(int ymin, int ymax); void searchByUsername(); void adminAdded(); private slots: void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); void peerUpdated(PeerData *peer); void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); void onAddBot(); void onAddAdmin(); void onNoAddAdminBox(QObject *obj); void onAllAdminsChanged(); protected: void paintEvent(QPaintEvent *e) override; void enterEvent(QEvent *e) override; void leaveEvent(QEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void resizeEvent(QResizeEvent *e) override; private: struct ContactData { ContactData() = default; ContactData(PeerData *peer, const base::lambda_copy &updateCallback); std_::unique_ptr checkbox; Text name; QString statusText; bool statusHasOnlineColor = false; bool disabledChecked = false; }; void init(); void initList(); void updateRowWithTop(int rowTop); int getSelectedRowTop() const; void updateSelectedRow(); int getRowTopWithPeer(PeerData *peer) const; void updateRowWithPeer(PeerData *peer); void addAdminDone(const MTPUpdates &result, mtpRequestId req); bool addAdminFail(const RPCError &error, mtpRequestId req); void paintDialog(Painter &p, uint64 ms, PeerData *peer, ContactData *data, bool sel); void paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, int y, int outerWidth) const; void changeCheckState(Dialogs::Row *row); void changeCheckState(ContactData *data, PeerData *peer); enum class ChangeStateWay { Default, SkipCallback, }; void changePeerCheckState(ContactData *data, PeerData *peer, bool checked, ChangeStateWay useCallback = ChangeStateWay::Default); template void addDialogsToList(FilterCallback callback); bool usingMultiSelect() const { return (_chat != nullptr) || (_creating != CreatingGroupNone && (!_channel || _membersFilter != MembersFilter::Admins)); } base::lambda _peerSelectedChangedCallback; int _rowHeight; int _aboutHeight = 0; ChatData *_chat = nullptr; ChannelData *_channel = nullptr; MembersFilter _membersFilter = MembersFilter::Recent; UserData *_bot = nullptr; CreatingGroupType _creating = CreatingGroupNone; MembersAlreadyIn _already; ChildWidget _allAdmins; int32 _aboutWidth; Text _aboutAllAdmins, _aboutAdmins; base::lambda _allAdminsChangedCallback; PeerData *_addToPeer = nullptr; UserData *_addAdmin = nullptr; mtpRequestId _addAdminRequestId = 0; ConfirmBox *_addAdminBox = nullptr; int32 _time; std_::unique_ptr _customList; Dialogs::IndexedList *_contacts = nullptr; Dialogs::Row *_sel = nullptr; QString _filter; using FilteredDialogs = QVector; FilteredDialogs _filtered; int _filteredSel = -1; bool _mouseSel = false; using ContactsData = QMap; ContactsData _contactsData; using CheckedContacts = OrderedSet; CheckedContacts _checkedContacts; ContactData *contactData(Dialogs::Row *row); bool _searching = false; QString _lastQuery; using ByUsernameRows = QVector; using ByUsernameDatas = QVector; ByUsernameRows _byUsername, _byUsernameFiltered; ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas ByUsernameDatas _byUsernameDatas; int _byUsernameSel = -1; QPoint _lastMousePos; ChildWidget _addContactLnk; bool _saving = false; bool _allAdminsChecked = false; };