Compare commits

...

235 Commits

Author SHA1 Message Date
John Preston
de230332b9 Version 1.8.14: Fix crash in text processing on old OS X. 2019-10-03 19:51:09 +03:00
John Preston
dcf79df0b2 Version 1.8.13.
- Bug fixes and other minor improvements.
2019-10-03 13:26:39 +03:00
John Preston
9773563926 Fix history geometry update being lost.
Fixes #6629.
2019-10-03 13:25:05 +03:00
John Preston
3779ad46ca Fix crash in pre-launch logging.
Fixes #6635.
2019-10-03 12:37:00 +03:00
John Preston
4e7946d03e Allow scales below 100.
Fixes #6632.
2019-10-03 12:34:58 +03:00
John Preston
606a90a4ac Fix crash in emoji suggestions.
Fixes #6636.
2019-10-03 12:13:41 +03:00
John Preston
af818b40aa Version 1.8.12: Apply user settings in Session().
Fixes #6617.
2019-10-02 13:47:40 +03:00
John Preston
ecfe1dacb2 Version 1.8.12: Fix build for Xcode. 2019-10-02 12:54:29 +03:00
John Preston
60640c7087 Version 1.8.12: Fix 'edited' badge display. 2019-10-02 12:08:31 +03:00
RadRussianRus
982edcb310 Resize message with photo when bubbled 2019-10-02 11:56:33 +03:00
Jiachen YANG
4669c07dc5 fix range-v3 version to 0.9.1 2019-10-02 11:52:55 +03:00
Jiachen YANG
c164985233 tweak vs2019 compiler settings to be compatible with newer range-v3 2019-10-02 11:52:55 +03:00
Jiachen YANG
497df7f4b2 change deprecated ranges::to_ to ranges::to (no underscore) 2019-10-02 11:52:55 +03:00
Jiachen YANG
56aab1aa07 explicitly specify the upper bound in ranges::view::ints 2019-10-02 11:52:55 +03:00
Jiachen YANG
8abeb4a9db include range/v3/range/conversion.hpp instead of range/v3/to_container.hpp 2019-10-02 11:52:55 +03:00
Jiachen YANG
d78716d7d4 appveyor use range-v3 latest branch 2019-10-02 11:52:55 +03:00
Jiachen YANG
d7489f8e67 Revert "Use 0.5.0 version of range-v3."
This reverts commit 3355e6da0c.
2019-10-02 11:52:55 +03:00
Jiachen YANG
9d850b71e7 be compatible with range-v3 0.9.x/1.0 branch
This made 2 changes to the current code base to be compatible with higher versions of range-v3 library.
1. ranges::iterator_range was renamed to subrange, see https://github.com/ericniebler/range-v3/issues/766
2. PercentCounterItem need an operator== for ranges::sort for some reason
2019-10-02 11:52:55 +03:00
RadRussianRus
684cfa16b8 Do not show edit timer for supergroup admins 2019-10-02 11:31:43 +03:00
John Preston
2cb7d76417 Optimize unixtime refresh. 2019-10-02 11:30:50 +03:00
23rd
9bf8b619fe Refactored ability to display media preview from touchbar. 2019-10-02 11:30:23 +03:00
John Preston
664162982c Version 1.8.12: Fix build for old OS X. 2019-10-02 11:21:56 +03:00
John Preston
718f862be0 Version 1.8.12: Fix build for Mac App Store. 2019-10-02 11:21:02 +03:00
John Preston
489bd22828 Version 1.8.12.
- Bug fixes and other minor improvements.
2019-10-02 11:18:23 +03:00
John Preston
65b5a29288 Fix local url handling.
Regression was introduced in 849deb57e2.

Fixes #6622.
2019-10-02 11:11:03 +03:00
John Preston
bf345da87b Version 1.8.11: Fix build for Xcode. 2019-10-02 00:57:16 +03:00
John Preston
1e5aa2a78d Version 1.8.11.
- Bug fixes and other minor improvements.
2019-10-01 23:59:46 +03:00
John Preston
8cf62c312f Allow not to cache emoji sprites. 2019-10-01 23:59:46 +03:00
John Preston
3b0bf7cb1e Move some more widgets and effects. 2019-10-01 23:59:46 +03:00
John Preston
673072ea5b Reorganize GYP files. 2019-10-01 23:59:46 +03:00
John Preston
849deb57e2 Move many widget classes to lib_ui. 2019-10-01 23:59:46 +03:00
John Preston
dda587a2fc Move QtConnectionOwner to base/qt_connection. 2019-10-01 23:59:46 +03:00
John Preston
c057f28425 Move standard buttons to lib_ui. 2019-10-01 23:59:46 +03:00
John Preston
a16c6ca41a Support more request types. 2019-10-01 23:59:46 +03:00
John Preston
c5845f17ae Remove twidget header. 2019-10-01 23:59:46 +03:00
John Preston
e2f54eb3e9 Move some style code to lib_ui. 2019-10-01 23:59:46 +03:00
John Preston
5a1c8e6a0a Move fonts to a separate .qrc file. 2019-10-01 23:59:46 +03:00
John Preston
b73390a3f6 Support emoji in popup menu items. 2019-10-01 23:59:46 +03:00
John Preston
0c713a930a Remove app.h / facades.h from precompiled header. 2019-10-01 23:59:46 +03:00
John Preston
a87529b8c9 Version 1.8.10.
- Bug fixes and other minor improvements.
2019-10-01 23:59:46 +03:00
John Preston
0c30bbf40a Fix file reference refresh in stickers / GIFs.
Fixes #6592.
2019-09-30 13:25:09 +03:00
John Preston
f75bfb4369 Version 1.8.9.
- Bug fixes and other minor improvements.
2019-09-27 20:04:25 +03:00
John Preston
0c34ba638c Don't show revoke checkbox in Saved Messages. 2019-09-27 20:01:36 +03:00
John Preston
4af9c801ae Don't show sticker suggestions if can't send.
Fixes #6581.
2019-09-27 19:56:54 +03:00
John Preston
e06bf16980 Improve name / title limits. 2019-09-27 19:46:27 +03:00
John Preston
1018745b0b Fix userpic display in custom notifications.
Fixes #6532.
2019-09-27 19:36:19 +03:00
John Preston
47b157bf32 Fix possible crash in custom notifications. 2019-09-27 18:53:44 +03:00
John Preston
09547bd6d5 Fix possible crash in scheduled messages. 2019-09-27 18:49:49 +03:00
RadRussianRus
d17f21c10d Use exact version of libxkbcommon in Travis build 2019-09-27 18:05:26 +03:00
udf
f11339361d Make "All Users" checkbox behave like the "All Actions" checkbox 2019-09-27 18:04:50 +03:00
John Preston
04b9d4bdb5 Fix animated emoji reloading. 2019-09-11 14:00:18 +03:00
John Preston
1d8d2b6251 Fix theme link preview title. 2019-09-10 18:30:47 +03:00
John Preston
190bf8fc5f Version 1.8.8.
- Create new themes based on your color and wallpaper choices.
- Share your themes with other users via links.
- Update your theme for all its users when you change something.
2019-09-10 14:22:20 +03:00
John Preston
f463d3ec6d Divide autoupdate paths for OS versions. 2019-09-10 14:22:20 +03:00
23rd
f9d10094ac Added warning when try to attach new media while editing message. 2019-09-10 14:22:20 +03:00
RadRussianRus
9fc87c3cb8 Fix last message tail drawing in theme preview 2019-09-10 10:48:20 +03:00
Sean
0d9c30423f Add chrpath to CMake instruction
Ref: 02a8b69
2019-09-10 10:13:51 +03:00
John Preston
c4bc76c022 Beta version 1.8.7.
- Bug fixes and other minor improvements.
2019-09-10 00:52:34 +03:00
John Preston
241f5e1d26 Make theme preview fit in minimal window size. 2019-09-10 00:50:15 +03:00
John Preston
76f0abecfd Show sticker set on Ctrl+Click in stickers list. 2019-09-10 00:48:34 +03:00
John Preston
ca45fb617e Fix applying background after theme edit. 2019-09-10 00:36:16 +03:00
John Preston
dfd63e66ff Add import theme to the editor. 2019-09-09 23:58:41 +03:00
John Preston
d85f162bff Fix saving cloud themes from media viewer. 2019-09-09 23:58:30 +03:00
John Preston
1d4fbc64e2 Ensure theme list radiobutton contrast. 2019-09-09 23:25:54 +03:00
John Preston
9fd32fc85b Ensure contrast in critical theme parts. 2019-09-09 23:02:24 +03:00
John Preston
f7f195eb98 Fix transfer ownership button visibility.
Regression was introduced in 569340c7d3.
2019-09-09 21:07:12 +03:00
John Preston
12c7bd8ee1 Suggest theme title based on accent color. 2019-09-09 20:27:07 +03:00
John Preston
cd5ef069c0 Ask to unlock Telegram before saving theme. 2019-09-09 20:26:53 +03:00
John Preston
f90fdce422 Fix crash on quit from passcode with a box. 2019-09-09 18:54:38 +03:00
John Preston
423254f7eb Fix theme editor closing in a small window.
Fixes #6510.
2019-09-09 18:37:04 +03:00
John Preston
c9c0d74b68 Fix build for Linux. 2019-09-09 17:50:26 +03:00
John Preston
8bf6013342 Add 'Export theme' / 'Show palette file' menu. 2019-09-09 17:44:08 +03:00
John Preston
c1ab1acd44 Fix crash in scheduled messages 'Send Now'. 2019-09-09 17:43:39 +03:00
John Preston
90b955534a Beta version 1.8.6: Improve theme preview. 2019-09-09 16:13:16 +03:00
John Preston
61c7bf2f5e Beta version 1.8.6.
- Bug fixes and other minor improvements.
2019-09-09 14:56:55 +03:00
John Preston
ee5423762a Generate previews for cloud theme links. 2019-09-09 14:56:05 +03:00
John Preston
6d29dc3b36 Apply themes in settings without preview. 2019-09-09 12:13:07 +03:00
John Preston
aa10934e85 Fix messing up accent color if editor is opened. 2019-09-09 12:02:06 +03:00
John Preston
9ff1fbcf47 Support theme document file origins. 2019-09-09 11:51:07 +03:00
John Preston
51c1dc20e1 Unregister randomId if send message fails. 2019-09-09 11:13:41 +03:00
John Preston
3a3bf84cfc Fix crash in SeparatePanel destruction.
We should destroy layers before panel widget destructor started.
We do it already for MainWindow in clearWidgetsHook.
2019-09-09 10:52:03 +03:00
John Preston
037b936613 Fix possible crash with emoji load failure. 2019-09-09 10:45:18 +03:00
John Preston
b631d09a40 Fix possible crash in event loop nesting. 2019-09-09 10:45:18 +03:00
John Zimmermann
fd4f384c3b Allow GDK wayland backend when Telegram is running under Wayland
and make sure that it never tries to load gtk2 under wayland, it can
lead to Telegram crashing
2019-09-09 10:25:01 +03:00
RadRussianRus
428a501bac Show Beginning button only for message date
Also hide button when you're already at beginning.
2019-09-09 10:17:23 +03:00
RadRussianRus
3ca57ae50d Added "Beginning" button to calendar 2019-09-09 10:17:23 +03:00
RadRussianRus
f1eddcd584 Make link preview title clickable 2019-09-09 10:15:32 +03:00
Sameer Hoosen
f979df3dfe Allow searching for messages by a user who has left a chat (#6417)
Fixes #5667
2019-09-09 10:05:29 +03:00
John Preston
eebcdb842d Generate previews async in cloud themes list. 2019-09-09 09:59:57 +03:00
John Preston
048658f838 Fix deleting theme from the list. 2019-09-09 08:54:17 +03:00
John Preston
8fd17e2e8f Fix theme with background with colorizer. 2019-09-09 08:53:16 +03:00
John Preston
5c13214244 Beta version 1.8.5: Fix build for old OS X. 2019-09-08 23:42:40 +03:00
John Preston
12a020cd09 Beta version 1.8.5: Fix build for Linux. 2019-09-08 23:38:45 +03:00
John Preston
b98e02f326 Beta version 1.8.5: Fix build on macOS. 2019-09-08 23:04:25 +03:00
John Preston
9a521c5340 Beta version 1.8.5.
- Create new themes based on your color and wallpaper choices.
- Share your themes with other users via links.
- Update your theme for all its users when you change something.
2019-09-08 21:38:58 +03:00
John Preston
5f26e92f5c Fix scheduled messages from ShareBox.
Fixes #6497.
2019-09-08 21:26:17 +03:00
John Preston
3d78c637a2 Strip editor information from palette. 2019-09-08 21:21:54 +03:00
John Preston
2ab2eed633 Push new saved theme to the list. 2019-09-08 21:05:35 +03:00
John Preston
bb7018424a Copy adjusted color values to editor. 2019-09-08 21:00:31 +03:00
John Preston
a770b5d4cd Allow share and remove cloud themes. 2019-09-08 20:32:25 +03:00
John Preston
c92a9585e1 Check changes when saving theme. 2019-09-08 19:29:43 +03:00
John Preston
cedb2d31af Check palette changes on editor cancel. 2019-09-08 16:40:15 +03:00
John Preston
95da2dbc34 Move create theme button to three-dot menu. 2019-09-08 15:47:22 +03:00
John Preston
f9b2a8d6ac Show progress in theme saving. 2019-09-08 14:52:42 +03:00
John Preston
a773ad7b02 Fix reverting edited theme values. 2019-09-06 19:31:07 +03:00
John Preston
32287a51f9 Don't show warning on theme autoupdate. 2019-09-06 19:31:07 +03:00
John Preston
37a4c79c81 Open theme preview after loading. 2019-09-06 19:31:07 +03:00
John Preston
95ee17bd54 Optimize theme edit data clearing. 2019-09-06 19:31:07 +03:00
John Preston
469c6770fb Handle t.me/addtheme links. 2019-09-06 19:31:07 +03:00
John Preston
e38123cc48 Send theme install requests. 2019-09-06 19:31:07 +03:00
John Preston
e3d7bf771f Update current theme in realtime. 2019-09-06 19:31:07 +03:00
John Preston
1cda90c3c5 Check fields before uploading theme. 2019-09-06 19:31:07 +03:00
John Preston
9c86f0e0a5 Save themes, apply and close editor on save. 2019-09-06 19:31:07 +03:00
John Preston
910f16312c Show not supported themes placeholders. 2019-09-06 19:31:07 +03:00
23rd
639b4bdd27 Fixed badge update during theme testing. 2019-09-06 19:31:07 +03:00
John Preston
4951eeac98 Allow creating desktop part of multi-theme. 2019-09-06 19:31:07 +03:00
John Preston
79106e0c01 Correctly show custom themes in list. 2019-09-06 19:31:07 +03:00
John Preston
7485f0c960 Don't change real theme when editing. 2019-09-06 19:31:07 +03:00
John Preston
03bdd80b2f Open theme editor for existing themes. 2019-09-06 19:31:07 +03:00
John Preston
dd74f57a66 Display full themes list in Settings. 2019-09-06 19:31:07 +03:00
John Preston
534772722e Improve theme selector design. 2019-09-06 19:31:07 +03:00
John Preston
6343221d7b Remove many includes from stdafx.pch. 2019-09-06 19:31:07 +03:00
John Preston
4929de2bfb Apply cloud themes. 2019-09-06 19:31:06 +03:00
John Preston
ac8f924909 Show tdesktop cloud themes in a box. 2019-09-06 19:31:06 +03:00
John Preston
95afcbb485 Format a valid theme file name. 2019-09-06 19:31:06 +03:00
John Preston
229bc56cc8 Upload saved theme to the cloud. 2019-09-06 19:31:06 +03:00
John Preston
4b045a602c Add a box for new theme creating. 2019-09-06 19:31:06 +03:00
John Preston
1e3b72ab74 Ignore background when checking which theme is used. 2019-09-06 19:31:06 +03:00
John Preston
bc63d9fe53 Fix check display in background box. 2019-09-06 19:31:06 +03:00
John Preston
d7cb8b7065 Version 1.8.4: Fix crash in local messages. 2019-09-06 16:41:43 +03:00
John Preston
30f4d870c5 Version 1.8.4: Fix crash on failed message delete. 2019-09-06 14:46:09 +03:00
John Preston
ad515c6f4a Version 1.8.4.
- Bug fixes and other minor improvements.
2019-09-06 14:39:04 +03:00
John Preston
056949416d Apply some rlottie fixes. 2019-09-06 14:37:41 +03:00
John Preston
ab6d9ff73c Fix sending first message in chat. 2019-09-06 14:14:04 +03:00
John Preston
8b766dda8e Fix crash in scheduled message in admin log.
Fixes #6495.
2019-09-06 12:20:59 +03:00
John Preston
ee0f66d746 Fix crash in history clear while sending messages.
Don't remove messages that are currently being sent from locals.
2019-09-06 10:56:01 +03:00
John Preston
7893ad0558 Mark all types of local history items. 2019-09-06 10:53:44 +03:00
John Preston
b0c5a75fb9 Version 1.8.3.
- Right click the 'Send' button and select 'Schedule Message'
to automatically send something at a specified time.
- Schedule reminders for yourself in the 'Saved Messages' chat.
- Get a notification when any of your scheduled messages are sent.
- Customize your app's appearance by choosing accent colors
for the 'Day', 'Night' and 'Tinted' themes.
- Choose who can find you on Telegram
when they add your number to their phone contacts.
- Send a single 😁, 😧, 😡, :poo:, 😢 or
😮 to check out what's new in the animated emoji department.
2019-09-05 21:49:20 +03:00
John Preston
1c313da888 Add dummy handler for addtheme links. 2019-09-05 21:49:20 +03:00
John Preston
ee210ea701 Update API scheme. 2019-09-05 20:42:45 +03:00
John Preston
e0c0d79be9 Fix build for GCC. 2019-09-02 15:22:01 +03:00
John Preston
cb3bad31fa Put 'Classic' theme first. 2019-09-02 14:27:22 +03:00
John Preston
917696be36 Closed alpha version 1.8.2.3. 2019-09-02 13:43:37 +03:00
John Preston
253816641c Submit by keyboard in scheduled section. 2019-09-02 13:39:43 +03:00
John Preston
ca2692473d Fix inactive window notifications clearing. 2019-09-02 13:39:43 +03:00
John Preston
8000dfac01 Fix crash on admin log exit by Escape. 2019-09-02 13:39:43 +03:00
John Preston
85cca51154 Make accent color radiobutton animated. 2019-09-02 13:39:43 +03:00
John Preston
87ea49e094 Allow schedule messages while in slowmode counter. 2019-09-02 13:39:43 +03:00
John Preston
66bf48e21e Remove 'Send without sound' in Saved messages. 2019-08-31 14:43:19 +03:00
John Preston
451056d2ab Track reminder unread status without cheating. 2019-08-31 14:32:15 +03:00
John Preston
791ae64a90 Fix crash in local messages tracking. 2019-08-31 14:25:24 +03:00
John Preston
3b4563772e Disable edit self restrictions in UI. 2019-08-31 14:25:02 +03:00
John Preston
16b786186b Fix schedule time selection. 2019-08-31 14:21:52 +03:00
John Preston
7be1c4ca2f Fix crash in right click on CalendarBox.
Any attempt to destroy a widget on right click will crash,
because Qt anyway sends QContextMenuEvent to the same widget.

Fixes #6464.
2019-08-31 14:15:30 +03:00
John Preston
17fba16c23 Fix crash on quit without initial passcode unlock. 2019-08-30 23:30:12 +03:00
John Preston
5fc4dcd172 Closed alpha version 1.8.2.2. 2019-08-30 18:45:07 +03:00
John Preston
b27a2cd34a Use edit_hide flag to hide 'edited' badge. 2019-08-30 18:00:32 +03:00
John Preston
569340c7d3 Move transfer ownership button. 2019-08-30 17:06:37 +03:00
John Preston
3715fa4b1e Fix scheduled notifications on inbox read. 2019-08-30 17:06:21 +03:00
John Preston
d95e54cb1a Check if it is possible to 'Send now.' 2019-08-30 16:17:46 +03:00
John Preston
470b67f557 Refresh file reference from scheduled messages. 2019-08-30 15:42:01 +03:00
John Preston
c46bcef9ff Add 'Send now' confirmation to langpack. 2019-08-30 15:24:42 +03:00
John Preston
c31cda0587 Use better accent color suggestions. 2019-08-30 13:32:15 +03:00
John Preston
5758f756c9 Fix draft clearing on message send.
Regression was introduced in caef7dde24.
2019-08-30 13:31:45 +03:00
John Preston
f199205592 Limit scheduled requests for one per minute. 2019-08-29 20:16:37 +03:00
John Preston
2b656f7745 Closed alpha version 1.8.2.1. 2019-08-29 16:16:22 +03:00
23rd
7be286751b Fixed text width in toast widget.
- Fixed #6381.
2019-08-29 16:16:22 +03:00
23rd
04617e4a12 Fixed display of tooltip for date of stickers.
- Fixed #6426.
 - Regression was introduced in b814c6307a.
2019-08-29 16:16:22 +03:00
John Preston
405ccb8580 Add editing of AddedByPhone privacy. 2019-08-29 11:44:03 +03:00
John Preston
131ef4f15a Fix palette generation on macOS. 2019-08-29 10:01:28 +03:00
John Preston
07f45b7eab Improve published scheduled notifications. 2019-08-29 10:01:28 +03:00
John Preston
87addd41b1 Add new phrases for reminders. 2019-08-29 10:01:28 +03:00
John Preston
0a4f91a53d Notify about published scheduled messages. 2019-08-29 10:01:28 +03:00
John Preston
3b76a908a4 Try to fix a crash in QNetworkReplyHttpImpl. 2019-08-29 10:01:28 +03:00
John Preston
9e3bc966c8 Fix crash in window destruction. 2019-08-29 10:01:28 +03:00
John Preston
149c69809d Apply peer restrictions. 2019-08-29 10:01:28 +03:00
John Preston
97a239a8b4 Show full emoji in fields in all scales. 2019-08-29 10:01:28 +03:00
John Preston
bd7cee2252 Improve accent color choose design. 2019-08-29 10:01:28 +03:00
John Preston
66d0d6e8fe Use HSV color space for colorizing. 2019-08-29 10:01:28 +03:00
John Preston
117d6192fa Ensure contrast in colorized themes. 2019-08-29 10:01:28 +03:00
John Preston
763bdf8798 Apply lightness limits in accent colors. 2019-08-29 10:01:28 +03:00
John Preston
56a82600f8 Add HSL color picker box for theming. 2019-08-29 10:01:28 +03:00
John Preston
a3e993253c Keep colorized theme in editor. 2019-08-29 10:01:28 +03:00
John Preston
04d5158ae3 Remove unused LeftOutlineButton type. 2019-08-29 10:01:28 +03:00
John Preston
38e4daacd4 Use HSL color space for accent colors. 2019-08-29 10:01:28 +03:00
John Preston
44d156760e Fix build in Xcode. 2019-08-27 19:10:37 +03:00
John Preston
d66541989e Improve first socket message. 2019-08-27 19:10:37 +03:00
John Preston
56c4d164f3 Fix crash in group creation. 2019-08-27 19:10:37 +03:00
John Preston
e2c1c4c8de Fix jump to first chat with archive. 2019-08-27 19:10:37 +03:00
John Preston
a465117689 Allow choosing accent colors from palette. 2019-08-27 19:10:37 +03:00
John Preston
c2117e7722 Save embedded themes accent colors. 2019-08-27 19:10:37 +03:00
John Preston
7de28fc4bd Apply saturation and value in colorizer. 2019-08-27 19:10:37 +03:00
John Preston
529ef64257 Sort colors by hue distance. 2019-08-27 19:10:37 +03:00
John Preston
9cb5423d40 Allow changing accent color in default themes. 2019-08-27 19:10:37 +03:00
John Preston
dd136350fb Fix save color in theme editor. 2019-08-27 19:10:37 +03:00
John Preston
ef7087348a Allow to schedule created polls. 2019-08-27 19:10:37 +03:00
John Preston
8eac2dcb78 Correctly show scheduled polls. 2019-08-27 19:10:37 +03:00
John Preston
f690f93f32 Allow schedule of files, stickers, GIFs. 2019-08-27 19:10:37 +03:00
John Preston
77ebdd3576 Send from scheduled messages section. 2019-08-27 19:10:37 +03:00
John Preston
3e895d0e85 Make tabbed selector working in scheduled section. 2019-08-27 19:10:37 +03:00
John Preston
385a7eb00d Start scheduled compose controls. 2019-08-27 19:10:37 +03:00
John Preston
1c9775baf9 Improve scheduled messages icon design. 2019-08-27 19:10:37 +03:00
John Preston
fb96d2eef8 Improve schedule box design. 2019-08-27 19:10:37 +03:00
John Preston
debeb61540 Start schedule message box. 2019-08-27 19:10:37 +03:00
John Preston
03cdddfe18 Allow sending scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
caef7dde24 Bundle silent and scheduled to Api::SendOptions. 2019-08-27 19:10:37 +03:00
John Preston
0b08810d5a Update hash counting for scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
694f771131 Allow to send scheduled messages instantly. 2019-08-27 19:10:37 +03:00
John Preston
956bb876f6 Sort scheduled messages on an update. 2019-08-27 19:10:37 +03:00
John Preston
99037d3d46 Improve scheduled messages top bar. 2019-08-27 19:10:37 +03:00
John Preston
ea0a616453 Allow deleting scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
815a18be94 Add initial scheduled list implementation. 2019-08-27 19:10:37 +03:00
John Preston
3814b0833d Keep track of scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
549789bfb7 Update API scheme to layer 105. 2019-08-27 19:10:37 +03:00
John Preston
a539fad3e2 Version 1.8.2: Fix passcode unlock. 2019-08-20 16:03:21 +03:00
John Preston
ee96d78656 Version 1.8.2: Fix build for Qt 5.3.2. 2019-08-20 14:29:21 +03:00
John Preston
288c1130b9 Version 1.8.2: Fix build. 2019-08-20 14:28:40 +03:00
John Preston
d1083a1fb4 Version 1.8.2.
- Bug fixes and other minor improvements.
2019-08-20 12:49:51 +03:00
John Preston
e1fe373504 Fix localtime-related slowmode errors. 2019-08-20 12:42:37 +03:00
23rd
1a06714f3a Added ability to apply formatting from Mac menu. 2019-08-20 12:02:37 +03:00
John Preston
7316d24ca4 Quick fix for 'Cant forward here' error. 2019-08-19 15:26:12 +03:00
John Preston
b814c6307a Check that sticker drag point is inside. 2019-08-19 15:26:12 +03:00
23rd
06fbb2edc4 Added permanent date display for sticker when fast action is enabled. 2019-08-19 15:26:12 +03:00
23rd
0ee47bb10f Fixed fast action button and date position for stickers. 2019-08-19 15:26:12 +03:00
23rd
9f228d8146 Fixed fast share button in messages with big emoji and reply. 2019-08-19 15:26:12 +03:00
23rd
de270d5283 Fixed right action click handler on stickers without stickerpack. 2019-08-19 15:26:12 +03:00
John Preston
3f041545bf Fix crash in local passcode reset. 2019-08-19 15:26:12 +03:00
John Preston
1139a59818 Fix crash in editing message to empty. 2019-08-19 15:26:12 +03:00
John Preston
1e64ba8f29 Fix app config refresh timeout. 2019-08-19 15:26:12 +03:00
John Preston
65342559c7 Use specific tag for libxkbcommon in docs. 2019-08-19 15:23:37 +03:00
John Preston
e150db1d0b Use specific tag for libkxbcommon in snap build. 2019-08-19 13:34:24 +03:00
John Preston
4548b14b45 Fix libvdpau repository link in snap build. 2019-08-19 13:15:32 +03:00
John Preston
dac1128dc9 Version 1.8.1.
- Bug fixes and other minor improvements.
2019-08-09 20:36:21 +01:00
John Preston
c3944d95b4 Fix local caching.
Regression was introduced in 9cf4cf6dca.

Fixes #6377.
2019-08-09 20:33:05 +01:00
John Preston
02b65a42f7 Move message client flags to a separate field. 2019-08-09 20:19:23 +01:00
703 changed files with 23894 additions and 10532 deletions

View File

@@ -28,7 +28,7 @@ GOTO:EOF
git clone -q --depth 1 --branch master https://github.com/telegramdesktop/dependencies_windows.git %LIB_DIR%
cd %LIB_DIR%
git clone --depth 1 --branch 0.5.0 https://github.com/ericniebler/range-v3
git clone --depth 1 --branch 0.9.1 https://github.com/ericniebler/range-v3
if exist prepare.bat (
call prepare.bat

View File

@@ -28,7 +28,7 @@ GYP_CACHE_VERSION="3"
GYP_PATCH="$UPSTREAM/Telegram/Patches/gyp.diff"
RANGE_PATH="$BUILD/range-v3"
RANGE_CACHE_VERSION="4"
RANGE_CACHE_VERSION="3"
VA_PATH="$BUILD/libva"
VA_CACHE_VERSION="3"
@@ -170,6 +170,7 @@ buildXkbCommon() {
git clone https://github.com/xkbcommon/libxkbcommon.git
cd "$EXTERNAL/libxkbcommon"
git checkout xkbcommon-0.8.4
./autogen.sh --prefix=$XKB_PATH
make $MAKE_ARGS
sudo make install
@@ -216,7 +217,7 @@ buildRange() {
rm -rf *
cd "$EXTERNAL"
git clone --depth 1 --branch 0.5.0 https://github.com/ericniebler/range-v3
git clone --depth 1 --branch 0.9.1 https://github.com/ericniebler/range-v3
cd "$EXTERNAL/range-v3"
cp -r * "$RANGE_PATH/"

View File

@@ -295,6 +295,40 @@ index c4cb8e65c0..45793e364f 100644
channels[i].socket->close();
delete channels[i].socket;
}
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 94235a48dd..9abd2cc0a1 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -2045,6 +2045,9 @@ void QNetworkReplyHttpImplPrivate::finished()
{
Q_Q(QNetworkReplyHttpImpl);
+ // Patch: Fix crash in Linux (by crashreports).
+ QPointer<QNetworkReplyHttpImpl> guard = q;
+
if (state == Finished || state == Aborted || state == WaitingForSession)
return;
@@ -2075,6 +2078,9 @@ void QNetworkReplyHttpImplPrivate::finished()
#endif
}
+ // Patch: Fix crash in Linux (by crashreports).
+ if (!guard) return;
+
// if we don't know the total size of or we received everything save the cache
if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
completeCacheSave();
@@ -2084,6 +2090,9 @@ void QNetworkReplyHttpImplPrivate::finished()
if (isHttpRedirectResponse() && errorCode == QNetworkReply::NoError)
return;
+ // Patch: Fix crash in Linux (by crashreports).
+ if (!guard) return;
+
state = Finished;
q->setFinished(true);
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 41834b21ae..8cdf4ab145 100644
--- a/src/network/socket/qnativesocketengine_win.cpp

View File

@@ -72,8 +72,11 @@ linkCropLimit: 360px;
linkFont: normalFont;
linkOverFont: font(fsize underline);
dateRadius: 6px;
buttonRadius: 3px;
roundRadiusLarge: 6px;
roundRadiusSmall: 3px;
dateRadius: roundRadiusLarge;
buttonRadius: roundRadiusSmall;
setLittleSkip: 9px;

View File

@@ -51,11 +51,6 @@ attentionButtonFgOver: #d14e4e; // default attention button text with mouse over
attentionButtonBgOver: #fcdfde; // default attention button background with mouse over
attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect
outlineButtonBg: windowBg; // default left outlined button background (like shared media links in profiles)
outlineButtonBgOver: lightButtonBgOver; // default left outlined button background with mouse over
outlineButtonOutlineFg: windowBgActive; // default left outlined button left outline border
outlineButtonBgRipple: lightButtonBgRipple; // default left outlined button ripple effect
menuBg: windowBg; // default popup menu background
menuBgOver: windowBgOver; // default popup menu item background with mouse over
menuBgRipple: windowBgRipple; // default popup menu item ripple effect

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -76,6 +76,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_month_day_year" = "{month} {day}, {year}";
"lng_month_year" = "{month} {year}";
"lng_calendar_beginning" = "Beginning";
"lng_box_ok" = "OK";
"lng_box_done" = "Done";
@@ -164,6 +166,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_media_album_error" = "This file cannot be saved as a part of an album.";
"lng_edit_media_invalid_file" = "Sorry, no way to use this file.";
"lng_edit_caption_attach" = "Sorry, you can't attach a new media while you're editing your message.";
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
"lng_start_msgs" = "START MESSAGING";
@@ -297,6 +300,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_notification_reply" = "Reply";
"lng_notification_hide_all" = "Hide all";
"lng_notification_sample" = "This is a sample notification";
"lng_notification_reminder" = "Reminder";
"lng_settings_section_general" = "General";
"lng_settings_change_lang" = "Change language";
@@ -336,7 +340,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_bg_use_default" = "Use default color theme";
"lng_settings_bg_from_gallery" = "Choose from gallery";
"lng_settings_bg_from_file" = "Choose from file";
"lng_settings_bg_edit_theme" = "Launch theme editor";
"lng_settings_bg_theme_edit" = "Edit theme";
"lng_settings_bg_theme_create" = "Create new theme";
"lng_settings_bg_cloud_themes" = "Custom themes";
"lng_settings_bg_show_all" = "Show all themes";
"lng_settings_bg_tile" = "Tile background";
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
@@ -385,10 +392,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_stickers_emoji" = "Stickers and emoji";
"lng_settings_messages" = "Messages";
"lng_settings_themes" = "Themes";
"lng_settings_theme_blue" = "Blue";
"lng_settings_theme_day" = "Day";
"lng_settings_theme_classic" = "Classic";
"lng_settings_theme_midnight" = "Midnight";
"lng_settings_theme_matrix" = "Matrix";
"lng_settings_theme_tinted" = "Tinted";
"lng_settings_theme_night" = "Night";
"lng_settings_theme_accent_title" = "Choose accent color";
"lng_settings_data_storage" = "Data and storage";
"lng_settings_information" = "Edit profile";
"lng_settings_passcode_title" = "Local passcode";
@@ -408,6 +416,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";
"lng_theme_keep_changes" = "Keep changes";
"lng_theme_revert" = "Revert";
"lng_theme_no_desktop" = "Sorry, this theme doesn't include a version for Telegram Desktop.";
"lng_theme_share" = "Share";
"lng_theme_edit" = "Edit";
"lng_theme_delete" = "Delete";
"lng_theme_delete_sure" = "Are you sure you want to delete this theme?";
"lng_background_header" = "Background preview";
"lng_background_text1" = "Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff!";
"lng_background_text2" = "I can't even take you seriously right now.";
@@ -629,6 +642,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_phone_number_always_title" = "Always share with";
"lng_edit_privacy_phone_number_never_title" = "Never share with";
"lng_edit_privacy_phone_number_find" = "Who can find me by my number";
"lng_edit_privacy_phone_number_contacts" = "Users who add your number to their contacts will see it on Telegram only if they are your contacts.";
"lng_edit_privacy_lastseen_title" = "Last seen privacy";
"lng_edit_privacy_lastseen_header" = "Who can see your last seen time";
"lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month).";
@@ -1134,6 +1150,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_size_limit" = "Limit by size";
"lng_media_size_up_to" = "up to {size}";
"lng_media_chat_background" = "Chat background";
"lng_media_color_theme" = "Color theme";
"lng_emoji_category1" = "People";
"lng_emoji_category2" = "Nature";
@@ -1240,6 +1257,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_send_button" = "Send";
"lng_send_silent_message" = "Send without sound";
"lng_schedule_message" = "Schedule message";
"lng_reminder_message" = "Set a reminder";
"lng_schedule_title" = "Send this message on...";
"lng_remind_title" = "Remind me on...";
"lng_schedule_at" = "at";
"lng_message_ph" = "Write a message...";
"lng_broadcast_ph" = "Broadcast a message...";
"lng_broadcast_silent_ph" = "Silent broadcast...";
@@ -1259,6 +1281,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_saved_short" = "Save";
"lng_saved_forward_here" = "Forward messages here for quick access";
"lng_scheduled_messages" = "Scheduled Messages";
"lng_reminder_messages" = "Reminders";
"lng_scheduled_send_now" = "Send message now?";
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
"lng_archived_name" = "Archived chats";
"lng_archived_add" = "Archive";
"lng_archived_remove" = "Unarchive";
@@ -1386,6 +1414,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_reply_msg" = "Reply";
"lng_context_edit_msg" = "Edit";
"lng_context_forward_msg" = "Forward Message";
"lng_context_send_now_msg" = "Send now";
"lng_context_delete_msg" = "Delete Message";
"lng_context_select_msg" = "Select Message";
"lng_context_report_msg" = "Report Message";
@@ -1395,6 +1424,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_copy_selected" = "Copy Selected Text";
"lng_context_copy_selected_items" = "Copy Selected as Text";
"lng_context_forward_selected" = "Forward Selected";
"lng_context_send_now_selected" = "Send selected now";
"lng_context_delete_selected" = "Delete Selected";
"lng_context_clear_selection" = "Clear Selection";
"lng_send_image_empty" = "Could not send an empty file: {name}";
@@ -1472,6 +1502,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_selected_clear" = "Cancel";
"lng_selected_delete" = "Delete";
"lng_selected_forward" = "Forward";
"lng_selected_send_now" = "Send now";
"lng_selected_cancel_sure_this" = "Cancel uploading?";
"lng_selected_upload_stop" = "Stop";
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
@@ -1536,6 +1567,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_preview_generating" = "Generating color theme preview...";
"lng_theme_preview_invalid" = "Invalid data in this theme file.";
"lng_theme_preview_apply" = "Apply this theme";
"lng_theme_preview_users#one" = "{count} person is using this theme";
"lng_theme_preview_users#other" = "{count} people are using this theme";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team";
@@ -1579,9 +1612,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_editor_save_palette" = "Save palette file";
"lng_theme_editor_choose_name" = "Save theme file";
"lng_theme_editor_error" = "The editor encountered an error :( See 'log.txt' for details.";
"lng_theme_editor_sure_close" = "Are you sure you want to close the editor? Your changes won't be saved.";
"lng_theme_editor_need_auth" = "You need to log in to save your theme.";
"lng_theme_editor_need_unlock" = "You need to unlock Telegram to save your theme.";
"lng_theme_editor_done" = "Theme exported successfully!";
"lng_theme_editor_title" = "Edit color palette";
"lng_theme_editor_export_button" = "Export theme";
"lng_theme_editor_save_button" = "Save theme";
"lng_theme_editor_create_title" = "Create theme";
"lng_theme_editor_attach_title" = "Attach desktop theme";
"lng_theme_editor_create" = "Create";
"lng_theme_editor_name" = "Theme name";
"lng_theme_editor_create_description" = "New theme will be based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
"lng_theme_editor_attach_description" = "You can create desktop part of your theme based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
"lng_theme_editor_import_existing" = "Import existing theme";
"lng_theme_editor_save_title" = "Save theme";
"lng_theme_editor_link_about" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be longer than 5 characters and use a-z, 0-9 and underscores.";
"lng_theme_editor_menu_export" = "Export theme";
"lng_theme_editor_menu_import" = "Import theme";
"lng_theme_editor_menu_show" = "Show palette file";
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
"lng_payments_receipt_label" = "Receipt";

View File

@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/gui">
<file alias="fonts/OpenSans-Regular.ttf">../fonts/OpenSans-Regular.ttf</file>
<file alias="fonts/OpenSans-Bold.ttf">../fonts/OpenSans-Bold.ttf</file>
<file alias="fonts/OpenSans-Semibold.ttf">../fonts/OpenSans-Semibold.ttf</file>
</qresource>
</RCC>

View File

@@ -1,71 +0,0 @@
<RCC>
<qresource prefix="/export">
<file alias="css/style.css">../export_html/css/style.css</file>
<file alias="images/back.png">../export_html/images/back.png</file>
<file alias="images/back@2x.png">../export_html/images/back@2x.png</file>
<file alias="images/media_call.png">../export_html/images/media_call.png</file>
<file alias="images/media_call@2x.png">../export_html/images/media_call@2x.png</file>
<file alias="images/media_contact.png">../export_html/images/media_contact.png</file>
<file alias="images/media_contact@2x.png">../export_html/images/media_contact@2x.png</file>
<file alias="images/media_file.png">../export_html/images/media_file.png</file>
<file alias="images/media_file@2x.png">../export_html/images/media_file@2x.png</file>
<file alias="images/media_game.png">../export_html/images/media_game.png</file>
<file alias="images/media_game@2x.png">../export_html/images/media_game@2x.png</file>
<file alias="images/media_location.png">../export_html/images/media_location.png</file>
<file alias="images/media_location@2x.png">../export_html/images/media_location@2x.png</file>
<file alias="images/media_music.png">../export_html/images/media_music.png</file>
<file alias="images/media_music@2x.png">../export_html/images/media_music@2x.png</file>
<file alias="images/media_photo.png">../export_html/images/media_photo.png</file>
<file alias="images/media_photo@2x.png">../export_html/images/media_photo@2x.png</file>
<file alias="images/media_shop.png">../export_html/images/media_shop.png</file>
<file alias="images/media_shop@2x.png">../export_html/images/media_shop@2x.png</file>
<file alias="images/media_video.png">../export_html/images/media_video.png</file>
<file alias="images/media_video@2x.png">../export_html/images/media_video@2x.png</file>
<file alias="images/media_voice.png">../export_html/images/media_voice.png</file>
<file alias="images/media_voice@2x.png">../export_html/images/media_voice@2x.png</file>
<file alias="images/section_calls.png">../export_html/images/section_calls.png</file>
<file alias="images/section_calls@2x.png">../export_html/images/section_calls@2x.png</file>
<file alias="images/section_chats.png">../export_html/images/section_chats.png</file>
<file alias="images/section_chats@2x.png">../export_html/images/section_chats@2x.png</file>
<file alias="images/section_contacts.png">../export_html/images/section_contacts.png</file>
<file alias="images/section_contacts@2x.png">../export_html/images/section_contacts@2x.png</file>
<file alias="images/section_frequent.png">../export_html/images/section_frequent.png</file>
<file alias="images/section_frequent@2x.png">../export_html/images/section_frequent@2x.png</file>
<file alias="images/section_other.png">../export_html/images/section_other.png</file>
<file alias="images/section_other@2x.png">../export_html/images/section_other@2x.png</file>
<file alias="images/section_photos.png">../export_html/images/section_photos.png</file>
<file alias="images/section_photos@2x.png">../export_html/images/section_photos@2x.png</file>
<file alias="images/section_sessions.png">../export_html/images/section_sessions.png</file>
<file alias="images/section_sessions@2x.png">../export_html/images/section_sessions@2x.png</file>
<file alias="images/section_web.png">../export_html/images/section_web.png</file>
<file alias="images/section_web@2x.png">../export_html/images/section_web@2x.png</file>
<file alias="js/script.js">../export_html/js/script.js</file>
</qresource>
<qresource prefix="/gui">
<file alias="fonts/OpenSans-Regular.ttf">../fonts/OpenSans-Regular.ttf</file>
<file alias="fonts/OpenSans-Bold.ttf">../fonts/OpenSans-Bold.ttf</file>
<file alias="fonts/OpenSans-Semibold.ttf">../fonts/OpenSans-Semibold.ttf</file>
<file alias="art/bg.jpg">../art/bg.jpg</file>
<file alias="art/bg_initial.jpg">../art/bg_initial.jpg</file>
<file alias="art/logo_256.png">../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../art/sunrise.jpg</file>
<file alias="day-blue.tdesktop-theme">../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../night-green.tdesktop-theme</file>
</qresource>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../sounds/msg_incoming.mp3</file>
<file alias="call_incoming.mp3">../sounds/call_incoming.mp3</file>
<file alias="call_outgoing.mp3">../sounds/call_outgoing.mp3</file>
<file alias="call_busy.mp3">../sounds/call_busy.mp3</file>
<file alias="call_connect.mp3">../sounds/call_connect.mp3</file>
<file alias="call_end.mp3">../sounds/call_end.mp3</file>
</qresource>
<qresource prefix="/qt-project.org">
<file>qmime/freedesktop.org.xml</file>
</qresource>
<qresource prefix="/misc">
<file alias="default_shortcuts-custom.json">../default_shortcuts-custom.json</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../../sounds/msg_incoming.mp3</file>
<file alias="call_busy.mp3">../../sounds/call_busy.mp3</file>
<file alias="call_connect.mp3">../../sounds/call_connect.mp3</file>
<file alias="call_end.mp3">../../sounds/call_end.mp3</file>
<file alias="call_incoming.mp3">../../sounds/call_incoming.mp3</file>
<file alias="call_outgoing.mp3">../../sounds/call_outgoing.mp3</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,60 @@
<RCC>
<qresource prefix="/export">
<file alias="css/style.css">../../export_html/css/style.css</file>
<file alias="images/back.png">../../export_html/images/back.png</file>
<file alias="images/back@2x.png">../../export_html/images/back@2x.png</file>
<file alias="images/media_call.png">../../export_html/images/media_call.png</file>
<file alias="images/media_call@2x.png">../../export_html/images/media_call@2x.png</file>
<file alias="images/media_contact.png">../../export_html/images/media_contact.png</file>
<file alias="images/media_contact@2x.png">../../export_html/images/media_contact@2x.png</file>
<file alias="images/media_file.png">../../export_html/images/media_file.png</file>
<file alias="images/media_file@2x.png">../../export_html/images/media_file@2x.png</file>
<file alias="images/media_game.png">../../export_html/images/media_game.png</file>
<file alias="images/media_game@2x.png">../../export_html/images/media_game@2x.png</file>
<file alias="images/media_location.png">../../export_html/images/media_location.png</file>
<file alias="images/media_location@2x.png">../../export_html/images/media_location@2x.png</file>
<file alias="images/media_music.png">../../export_html/images/media_music.png</file>
<file alias="images/media_music@2x.png">../../export_html/images/media_music@2x.png</file>
<file alias="images/media_photo.png">../../export_html/images/media_photo.png</file>
<file alias="images/media_photo@2x.png">../../export_html/images/media_photo@2x.png</file>
<file alias="images/media_shop.png">../../export_html/images/media_shop.png</file>
<file alias="images/media_shop@2x.png">../../export_html/images/media_shop@2x.png</file>
<file alias="images/media_video.png">../../export_html/images/media_video.png</file>
<file alias="images/media_video@2x.png">../../export_html/images/media_video@2x.png</file>
<file alias="images/media_voice.png">../../export_html/images/media_voice.png</file>
<file alias="images/media_voice@2x.png">../../export_html/images/media_voice@2x.png</file>
<file alias="images/section_calls.png">../../export_html/images/section_calls.png</file>
<file alias="images/section_calls@2x.png">../../export_html/images/section_calls@2x.png</file>
<file alias="images/section_chats.png">../../export_html/images/section_chats.png</file>
<file alias="images/section_chats@2x.png">../../export_html/images/section_chats@2x.png</file>
<file alias="images/section_contacts.png">../../export_html/images/section_contacts.png</file>
<file alias="images/section_contacts@2x.png">../../export_html/images/section_contacts@2x.png</file>
<file alias="images/section_frequent.png">../../export_html/images/section_frequent.png</file>
<file alias="images/section_frequent@2x.png">../../export_html/images/section_frequent@2x.png</file>
<file alias="images/section_other.png">../../export_html/images/section_other.png</file>
<file alias="images/section_other@2x.png">../../export_html/images/section_other@2x.png</file>
<file alias="images/section_photos.png">../../export_html/images/section_photos.png</file>
<file alias="images/section_photos@2x.png">../../export_html/images/section_photos@2x.png</file>
<file alias="images/section_sessions.png">../../export_html/images/section_sessions.png</file>
<file alias="images/section_sessions@2x.png">../../export_html/images/section_sessions@2x.png</file>
<file alias="images/section_web.png">../../export_html/images/section_web.png</file>
<file alias="images/section_web@2x.png">../../export_html/images/section_web@2x.png</file>
<file alias="js/script.js">../../export_html/js/script.js</file>
</qresource>
<qresource prefix="/gui">
<file alias="art/bg.jpg">../../art/bg.jpg</file>
<file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file>
<file alias="art/logo_256.png">../../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../../art/sunrise.jpg</file>
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
</qresource>
<qresource prefix="/qt-project.org">
<file>../qmime/freedesktop.org.xml</file>
</qresource>
<qresource prefix="/misc">
<file alias="default_shortcuts-custom.json">../../default_shortcuts-custom.json</file>
</qresource>
</RCC>

View File

@@ -1,10 +0,0 @@
<RCC>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../sounds/msg_incoming.mp3</file>
<file alias="call_busy.mp3">../sounds/call_busy.mp3</file>
<file alias="call_connect.mp3">../sounds/call_connect.mp3</file>
<file alias="call_end.mp3">../sounds/call_end.mp3</file>
<file alias="call_incoming.mp3">../sounds/call_incoming.mp3</file>
<file alias="call_outgoing.mp3">../sounds/call_outgoing.mp3</file>
</qresource>
</RCC>

View File

@@ -108,7 +108,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#200250ba id:int = User;
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
@@ -123,11 +123,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#7328bdb id:int title:string = Chat;
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -140,7 +140,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
@@ -222,7 +222,7 @@ inputReportReasonOther#e1746d0a text:string = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
contact#f911c994 user_id:int mutual:Bool = Contact;
@@ -344,6 +344,9 @@ updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBanne
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
updateNewScheduledMessage#39a51dfb message:Message = Update;
updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
updateTheme#8216fba3 theme:Theme = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -440,6 +443,7 @@ inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
privacyKeyChatInvite#500e6dfa = PrivacyKey;
@@ -448,6 +452,7 @@ privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
privacyKeyForwards#69ec56a3 = PrivacyKey;
privacyKeyProfilePhoto#96151fed = PrivacyKey;
privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
@@ -491,7 +496,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
webPageEmpty#eb1477e8 id:long = WebPage;
webPagePending#c586da1c id:long date:int = WebPage;
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
webPage#fa64e172 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document documents:flags.11?Vector<Document> cached_page:flags.10?Page = WebPage;
webPageNotModified#85849473 = WebPage;
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
@@ -1062,6 +1067,17 @@ channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
inputThemeSlug#f5890df1 slug:string = InputTheme;
themeDocumentNotModified#483d270c = Theme;
theme#f7d90ce0 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document installs_count:int = Theme;
account.themesNotModified#f41eb622 = account.Themes;
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1143,6 +1159,13 @@ account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSet
account.resetWallPapers#bb3b9804 = Bool;
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;
account.createTheme#2b7ffd7f slug:string title:string document:InputDocument = Theme;
account.updateTheme#3b8ea202 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument = Theme;
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
account.getThemes#285946f8 format:string hash:int = account.Themes;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@@ -1177,9 +1200,9 @@ messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
@@ -1223,9 +1246,9 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates;
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
@@ -1259,7 +1282,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages;
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@@ -1281,6 +1304,10 @@ messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter
messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1388,4 +1415,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates;
// LAYER 104
// LAYER 105

View File

@@ -99,6 +99,7 @@ tlsBlockRandom length:int = TlsBlock;
tlsBlockZero length:int = TlsBlock;
tlsBlockDomain = TlsBlock;
tlsBlockGrease seed:int = TlsBlock;
tlsBlockPublicKey = TlsBlock;
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
---functions---

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="1.8.0.0" />
Version="1.8.14.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>

View File

@@ -1,6 +1,5 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
@@ -34,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,8,0,0
PRODUCTVERSION 1,8,0,0
FILEVERSION 1,8,14,0
PRODUCTVERSION 1,8,14,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -52,10 +51,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.8.0.0"
VALUE "FileVersion", "1.8.14.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.8.0.0"
VALUE "ProductVersion", "1.8.14.0"
END
END
BLOCK "VarFileInfo"
@@ -66,16 +65,3 @@ END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -1,6 +1,5 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource1.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
@@ -25,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,8,0,0
PRODUCTVERSION 1,8,0,0
FILEVERSION 1,8,14,0
PRODUCTVERSION 1,8,14,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -43,10 +42,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.8.0.0"
VALUE "FileVersion", "1.8.14.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.8.0.0"
VALUE "ProductVersion", "1.8.14.0"
END
END
BLOCK "VarFileInfo"
@@ -57,16 +56,3 @@ END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

Binary file not shown.

View File

@@ -1,14 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Updater.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,46 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
namespace Api {
struct SendOptions {
TimeId scheduled = 0;
bool silent = false;
bool handleSupportSwitch = false;
bool removeWebPageId = false;
};
enum class SendType {
Normal,
Scheduled,
};
struct SendAction {
explicit SendAction(not_null<History*> history) : history(history) {
}
not_null<History*> history;
SendOptions options;
MsgId replyTo = 0;
bool clearDraft = true;
bool generateLocal = true;
};
struct MessageToSend {
explicit MessageToSend(not_null<History*> history) : action(history) {
}
SendAction action;
TextWithTags textWithTags;
WebPageId webPageId = 0;
};
} // namespace Api

View File

@@ -0,0 +1,42 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Api {
[[nodiscard]] inline uint32 HashInit() {
return 0;
}
inline void HashUpdate(uint32 &already, uint32 value) {
already = (already * 20261) + uint32(value);
}
inline void HashUpdate(uint32 &already, int32 value) {
HashUpdate(already, uint32(value));
}
inline void HashUpdate(uint32 &already, uint64 value) {
HashUpdate(already, uint32(value >> 32));
HashUpdate(already, uint32(value & 0xFFFFFFFFULL));
}
[[nodiscard]] inline int32 HashFinalize(uint32 already) {
return int32(already & 0x7FFFFFFF);
}
template <typename IntRange>
[[nodiscard]] inline int32 CountHash(IntRange &&range) {
auto result = HashInit();
for (const auto value : range) {
HashUpdate(result, value);
}
return HashFinalize(result);
}
} // namespace Api

View File

@@ -7,55 +7,55 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_sending.h"
#include "api/api_text_entities.h"
#include "base/unixtime.h"
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_channel.h" // ChannelData::addsSignature.
#include "data/data_user.h" // App::peerName(UserData*).
#include "data/data_user.h" // UserData::name
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "history/history.h"
#include "history/history_message.h" // NewMessageFlags.
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
#include "ui/text/text_entity.h" // TextWithEntities.
#include "main/main_session.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "app.h"
namespace Api {
namespace {
template <typename MediaData>
void SendExistingMedia(
not_null<History*> history,
Api::MessageToSend &&message,
not_null<MediaData*> media,
const MTPInputMedia &inputMedia,
Data::FileOrigin origin,
TextWithEntities caption,
MsgId replyToId,
bool silent) {
Fn<MTPInputMedia()> inputMedia,
Data::FileOrigin origin) {
const auto history = message.action.history;
const auto peer = history->peer;
const auto session = &history->session();
const auto api = &session->api();
auto options = ApiWrap::SendOptions(history);
options.clearDraft = false;
options.replyTo = replyToId;
options.generateLocal = true;
options.silent = silent;
message.action.clearDraft = false;
message.action.generateLocal = true;
api->sendAction(message.action);
api->sendAction(options);
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
const auto newId = FullMsgId(
peerToChannel(peer->id),
session->data().nextLocalMessageId());
const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (options.replyTo) {
if (message.action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = options.silent
const auto silentPost = message.action.options.silent
|| (channelPost && session->data().notifySilentPosts(peer));
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
@@ -70,28 +70,38 @@ void SendExistingMedia(
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : session->userId();
auto messagePostAuthor = channelPost
? App::peerName(session->user())
: QString();
auto messagePostAuthor = channelPost ? session->user()->name : QString();
auto caption = TextWithEntities{
message.textWithTags.text,
TextUtilities::ConvertTextTagsToEntities(message.textWithTags.tags)
};
TextUtilities::Trim(caption);
auto sentEntities = TextUtilities::EntitiesToMTP(
auto sentEntities = EntitiesToMTP(
caption.entities,
TextUtilities::ConvertOption::SkipLocal);
ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
const auto replyTo = options.replyTo;
const auto replyTo = message.action.replyTo;
const auto captionText = caption.text;
if (message.action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
session->data().registerMessageRandomId(randomId, newId);
history->addNewLocalMessage(
newId.msg,
flags,
clientFlags,
0,
replyTo,
base::unixtime::now(),
HistoryItem::NewMessageDate(message.action.options.scheduled),
messageFromId,
messagePostAuthor,
media,
@@ -105,11 +115,12 @@ void SendExistingMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
inputMedia,
inputMedia(),
MTP_string(captionText),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities
sentEntities,
MTP_int(message.action.options.scheduled)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
@@ -124,46 +135,36 @@ void SendExistingMedia(
if (media->fileReference() != usedFileReference) {
performRequest();
} else {
api->sendMessageFail(error, peer, newId);
api->sendMessageFail(error, peer, randomId, newId);
}
});
} else {
api->sendMessageFail(error, peer, newId);
api->sendMessageFail(error, peer, randomId, newId);
}
};
performRequest();
if (const auto main = App::main()) {
main->finishForwarding(history, options.silent);
main->finishForwarding(message.action);
}
}
} // namespace
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document,
bool silent) {
SendExistingDocument(history, document, {}, 0, silent);
}
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document,
TextWithEntities caption,
MsgId replyToId,
bool silent) {
SendExistingMedia(
history,
document,
MTP_inputMediaDocument(
Api::MessageToSend &&message,
not_null<DocumentData*> document) {
const auto inputMedia = [=] {
return MTP_inputMediaDocument(
MTP_flags(0),
document->mtpInput(),
MTPint()),
document->stickerOrGifOrigin(),
caption,
replyToId,
silent);
MTPint());
};
SendExistingMedia(
std::move(message),
document,
inputMedia,
document->stickerOrGifOrigin());
if (document->sticker()) {
if (const auto main = App::main()) {
@@ -174,29 +175,19 @@ void SendExistingDocument(
}
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo,
bool silent) {
SendExistingPhoto(history, photo, {}, 0, silent);
}
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo,
TextWithEntities caption,
MsgId replyToId,
bool silent) {
SendExistingMedia(
history,
photo,
MTP_inputMediaPhoto(
Api::MessageToSend &&message,
not_null<PhotoData*> photo) {
const auto inputMedia = [=] {
return MTP_inputMediaPhoto(
MTP_flags(0),
photo->mtpInput(),
MTPint()),
Data::FileOrigin(),
caption,
replyToId,
silent);
MTPint());
};
SendExistingMedia(
std::move(message),
photo,
inputMedia,
Data::FileOrigin());
}
} // namespace Api

View File

@@ -9,32 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History;
class DocumentData;
struct TextWithEntities;
namespace Api {
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document,
bool silent = false);
struct MessageToSend;
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document,
TextWithEntities caption,
MsgId replyToId = 0,
bool silent = false);
Api::MessageToSend &&message,
not_null<DocumentData*> document);
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo,
bool silent = false);
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo,
TextWithEntities caption,
MsgId replyToId = 0,
bool silent = false);
Api::MessageToSend &&message,
not_null<PhotoData*> photo);
} // namespace Api

View File

@@ -0,0 +1,129 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_user.h"
namespace Api {
namespace {
using namespace TextUtilities;
} // namespace
EntitiesInText EntitiesFromMTP(const QVector<MTPMessageEntity> &entities) {
auto result = EntitiesInText();
if (!entities.isEmpty()) {
result.reserve(entities.size());
for_const (auto &entity, entities) {
switch (entity.type()) {
case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break;
case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPhone: break; // Skipping phones.
case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityMentionName: {
auto &d = entity.c_messageEntityMentionName();
auto data = [&d] {
if (auto user = Auth().data().userLoaded(d.vuser_id().v)) {
return MentionNameDataFromFields({
d.vuser_id().v,
user->accessHash() });
}
return MentionNameDataFromFields(d.vuser_id().v);
};
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data() });
} break;
case mtpc_inputMessageEntityMentionName: {
auto &d = entity.c_inputMessageEntityMentionName();
auto data = ([&d]() -> QString {
if (d.vuser_id().type() == mtpc_inputUserSelf) {
return MentionNameDataFromFields(Auth().userId());
} else if (d.vuser_id().type() == mtpc_inputUser) {
auto &user = d.vuser_id().c_inputUser();
return MentionNameDataFromFields({ user.vuser_id().v, user.vaccess_hash().v });
}
return QString();
})();
if (!data.isEmpty()) {
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
}
} break;
case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break;
// #TODO entities
}
}
}
return result;
}
MTPVector<MTPMessageEntity> EntitiesToMTP(
const EntitiesInText &entities,
ConvertOption option) {
auto v = QVector<MTPMessageEntity>();
v.reserve(entities.size());
for_const (auto &entity, entities) {
if (entity.length() <= 0) continue;
if (option == ConvertOption::SkipLocal
&& entity.type() != EntityType::Bold
&& entity.type() != EntityType::Italic
&& entity.type() != EntityType::Underline
&& entity.type() != EntityType::StrikeOut
&& entity.type() != EntityType::Code // #TODO entities
&& entity.type() != EntityType::Pre
&& entity.type() != EntityType::MentionName
&& entity.type() != EntityType::CustomUrl) {
continue;
}
auto offset = MTP_int(entity.offset());
auto length = MTP_int(entity.length());
switch (entity.type()) {
case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break;
case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break;
case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break;
case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break;
case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break;
case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break;
case EntityType::MentionName: {
auto inputUser = ([](const QString &data) -> MTPInputUser {
auto fields = MentionNameDataToFields(data);
if (fields.userId == Auth().userId()) {
return MTP_inputUserSelf();
} else if (fields.userId) {
return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash));
}
return MTP_inputUserEmpty();
})(entity.data());
if (inputUser.type() != mtpc_inputUserEmpty) {
v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser));
}
} break;
case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break;
case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break;
case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break;
case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break;
case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break;
case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities
case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break;
}
}
return MTP_vector<MTPMessageEntity>(std::move(v));
}
} // namespace Api

View File

@@ -0,0 +1,23 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/text/text_entity.h"
namespace Api {
EntitiesInText EntitiesFromMTP(const QVector<MTPMessageEntity> &entities);
enum class ConvertOption {
WithLocal,
SkipLocal,
};
MTPVector<MTPMessageEntity> EntitiesToMTP(
const EntitiesInText &entities,
ConvertOption option = ConvertOption::WithLocal);
} // namespace Api

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "apiwrap.h"
#include "api/api_text_entities.h"
#include "data/data_drafts.h"
#include "data/data_photo.h"
#include "data/data_web_page.h"
@@ -15,11 +16,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h"
#include "data/data_sparse_ids.h"
#include "data/data_search_controller.h"
#include "data/data_scheduled_messages.h"
#include "data/data_channel_admins.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_cloud_themes.h"
#include "dialogs/dialogs_key.h"
#include "core/core_cloud_password.h"
#include "core/application.h"
@@ -56,6 +59,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "storage/storage_user_photos.h"
#include "storage/storage_media_prepare.h"
#include "facades.h"
#include "app.h"
//#include "storage/storage_feed_messages.h" // #feed
namespace {
@@ -156,19 +161,13 @@ MTPVector<MTPDocumentAttribute> ComposeSendingDocumentAttributes(
} // namespace
ApiWrap::SendOptions::SendOptions(not_null<History*> history)
: history(history) {
}
ApiWrap::MessageToSend::MessageToSend(not_null<History*> history)
: history(history) {
}
MTPInputPrivacyKey ApiWrap::Privacy::Input(Key key) {
switch (key) {
case Privacy::Key::Calls: return MTP_inputPrivacyKeyPhoneCall();
case Privacy::Key::Invites: return MTP_inputPrivacyKeyChatInvite();
case Privacy::Key::PhoneNumber: return MTP_inputPrivacyKeyPhoneNumber();
case Privacy::Key::AddedByPhone:
return MTP_inputPrivacyKeyAddedByPhone();
case Privacy::Key::LastSeen:
return MTP_inputPrivacyKeyStatusTimestamp();
case Privacy::Key::CallsPeer2Peer:
@@ -187,6 +186,8 @@ std::optional<ApiWrap::Privacy::Key> ApiWrap::Privacy::KeyFromMTP(
switch (type) {
case mtpc_privacyKeyPhoneNumber:
case mtpc_inputPrivacyKeyPhoneNumber: return Key::PhoneNumber;
case mtpc_privacyKeyAddedByPhone:
case mtpc_inputPrivacyKeyAddedByPhone: return Key::AddedByPhone;
case mtpc_privacyKeyStatusTimestamp:
case mtpc_inputPrivacyKeyStatusTimestamp: return Key::LastSeen;
case mtpc_privacyKeyChatInvite:
@@ -544,6 +545,7 @@ void ApiWrap::toggleHistoryArchived(
void ApiWrap::sendMessageFail(
const RPCError &error,
not_null<PeerData*> peer,
uint64 randomId,
FullMsgId itemId) {
if (error.type() == qstr("PEER_FLOOD")) {
Ui::show(Box<InformBox>(
@@ -561,11 +563,17 @@ void ApiWrap::sendMessageFail(
const auto left = error.type().mid(chop).toInt();
if (const auto channel = peer->asChannel()) {
const auto seconds = channel->slowmodeSeconds();
channel->growSlowmodeLastMessage(
base::unixtime::now() - (left - seconds));
if (seconds >= left) {
channel->growSlowmodeLastMessage(
base::unixtime::now() - (left - seconds));
} else {
requestFullPeer(peer);
}
}
}
if (const auto item = _session->data().message(itemId)) {
Assert(randomId != 0);
session().data().unregisterMessageRandomId(randomId);
item->sendFailed();
}
}
@@ -2555,9 +2563,9 @@ void ApiWrap::saveDraftsToCloud() {
if (!textWithTags.tags.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_entities;
}
auto entities = TextUtilities::EntitiesToMTP(
ConvertTextTagsToEntities(textWithTags.tags),
TextUtilities::ConvertOption::SkipLocal);
auto entities = Api::EntitiesToMTP(
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
Api::ConvertOption::SkipLocal);
const auto draftText = textWithTags.text;
history->setSentDraftText(draftText);
@@ -2996,7 +3004,13 @@ void ApiWrap::refreshFileReference(
};
origin.data.match([&](Data::FileOriginMessage data) {
if (const auto item = _session->data().message(data)) {
if (const auto channel = item->history()->peer->asChannel()) {
if (item->isScheduled()) {
const auto &scheduled = session().data().scheduledMessages();
const auto realId = scheduled.lookupId(item);
request(MTPmessages_GetScheduledMessages(
item->history()->peer->input,
MTP_vector<MTPint>(1, MTP_int(realId))));
} else if (const auto channel = item->history()->peer->asChannel()) {
request(MTPchannels_GetMessages(
channel->inputChannel,
MTP_vector<MTPInputMessage>(
@@ -3053,6 +3067,13 @@ void ApiWrap::refreshFileReference(
MTP_inputWallPaper(
MTP_long(data.paperId),
MTP_long(data.accessHash))));
}, [&](Data::FileOriginTheme data) {
request(MTPaccount_GetTheme(
MTP_string(Data::CloudThemes::Format()),
MTP_inputTheme(
MTP_long(data.themeId),
MTP_long(data.accessHash)),
MTP_long(0)));
}, [&](std::nullopt_t) {
fail();
});
@@ -3103,6 +3124,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
for (const auto &[position, index] : indices) {
const auto item = _session->data().addNewMessage(
v->at(index),
MTPDmessage_ClientFlags(),
NewMessageType::Existing);
if (item) {
_session->data().requestItemResize(item);
@@ -3548,7 +3570,8 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
switch (updates.type()) {
case mtpc_updateShortMessage: {
const auto &d = updates.c_updateShortMessage();
const auto flags = mtpCastFlags(d.vflags().v) | MTPDmessage::Flag::f_from_id;
const auto flags = mtpCastFlags(d.vflags().v)
| MTPDmessage::Flag::f_from_id;
const auto peerUserId = d.is_out()
? d.vuser_id()
: MTP_int(_session->userId());
@@ -3570,7 +3593,10 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPint(),
MTPint(),
MTPstring(),
MTPlong()),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
} break;
@@ -3595,7 +3621,10 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPint(),
MTPint(),
MTPstring(),
MTPlong()),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
} break;
@@ -3620,7 +3649,10 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
}
}
if (needToAdd) {
_session->data().addNewMessage(d.vmessage(), NewMessageType::Unread);
_session->data().addNewMessage(
d.vmessage(),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
}
} break;
@@ -3709,7 +3741,10 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
}
}
if (needToAdd) {
_session->data().addNewMessage(d.vmessage(), NewMessageType::Unread);
_session->data().addNewMessage(
d.vmessage(),
MTPDmessage_ClientFlags(),
NewMessageType::Unread);
}
} break;
@@ -4368,15 +4403,15 @@ void ApiWrap::userPhotosDone(
// )).send();
//}
void ApiWrap::sendAction(const SendOptions &options) {
readServerHistory(options.history);
options.history->getReadyFor(ShowAtTheEndMsgId);
_sendActions.fire_copy(options);
void ApiWrap::sendAction(const SendAction &action) {
readServerHistory(action.history);
action.history->getReadyFor(ShowAtTheEndMsgId);
_sendActions.fire_copy(action);
}
void ApiWrap::forwardMessages(
HistoryItemsList &&items,
const SendOptions &options,
const SendAction &action,
FnMut<void()> &&successCallback) {
Expects(!items.empty());
@@ -4392,17 +4427,18 @@ void ApiWrap::forwardMessages(
}
const auto count = int(items.size());
const auto genClientSideMessage = options.generateLocal && (count < 2);
const auto history = options.history;
const auto genClientSideMessage = action.generateLocal && (count < 2);
const auto history = action.history;
const auto peer = history->peer;
readServerHistory(history);
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = options.silent
const auto silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
auto flags = MTPDmessage::Flags(0);
auto clientFlags = MTPDmessage_ClientFlags();
auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
@@ -4416,12 +4452,18 @@ void ApiWrap::forwardMessages(
if (silentPost) {
sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
}
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_ForwardMessages::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
auto forwardFrom = items.front()->history()->peer;
auto currentGroupId = items.front()->groupId();
auto ids = QVector<MTPint>();
auto randomIds = QVector<MTPlong>();
auto localIds = std::unique_ptr<std::vector<FullMsgId>>();
auto localIds = std::unique_ptr<base::flat_map<uint64, FullMsgId>>();
const auto sendAccumulated = [&] {
if (shared) {
@@ -4436,7 +4478,8 @@ void ApiWrap::forwardMessages(
forwardFrom->input,
MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds),
peer->input
peer->input,
MTP_int(action.options.scheduled)
)).done([=, callback = std::move(successCallback)](
const MTPUpdates &updates) {
applyUpdates(updates);
@@ -4445,8 +4488,8 @@ void ApiWrap::forwardMessages(
}
}).fail([=, ids = std::move(localIds)](const RPCError &error) {
if (ids) {
for (const auto &itemId : *ids) {
sendMessageFail(error, peer, itemId);
for (const auto &[randomId, itemId] : *ids) {
sendMessageFail(error, peer, randomId, itemId);
}
} else {
sendMessageFail(error, peer);
@@ -4463,31 +4506,32 @@ void ApiWrap::forwardMessages(
ids.reserve(count);
randomIds.reserve(count);
for (const auto item : items) {
auto randomId = rand_value<uint64>();
const auto randomId = rand_value<uint64>();
if (genClientSideMessage) {
if (const auto message = item->toHistoryMessage()) {
const auto newId = FullMsgId(
peerToChannel(peer->id),
clientMsgId());
session().data().nextLocalMessageId());
const auto self = _session->user();
const auto messageFromId = channelPost
? UserId(0)
: peerToUser(self->id);
const auto messagePostAuthor = channelPost
? App::peerName(self)
? self->name
: QString();
history->addNewLocalMessage(
newId.msg,
flags,
base::unixtime::now(),
clientFlags,
HistoryItem::NewMessageDate(action.options.scheduled),
messageFromId,
messagePostAuthor,
message);
_session->data().registerMessageRandomId(randomId, newId);
if (!localIds) {
localIds = std::make_unique<std::vector<FullMsgId>>();
localIds = std::make_unique<base::flat_map<uint64, FullMsgId>>();
}
localIds->push_back(newId);
localIds->emplace(randomId, newId);
}
}
const auto newFrom = item->history()->peer;
@@ -4509,14 +4553,14 @@ void ApiWrap::shareContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
const SendOptions &options) {
const SendAction &action) {
const auto userId = UserId(0);
sendSharedContact(phone, firstName, lastName, userId, options);
sendSharedContact(phone, firstName, lastName, userId, action);
}
void ApiWrap::shareContact(
not_null<UserData*> user,
const SendOptions &options) {
const SendAction &action) {
const auto userId = peerToUser(user->id);
const auto phone = _session->data().findContactPhone(user);
if (phone.isEmpty()) {
@@ -4527,7 +4571,7 @@ void ApiWrap::shareContact(
user->firstName,
user->lastName,
userId,
options);
action);
}
void ApiWrap::sendSharedContact(
@@ -4535,17 +4579,20 @@ void ApiWrap::sendSharedContact(
const QString &firstName,
const QString &lastName,
UserId userId,
const SendOptions &options) {
sendAction(options);
const SendAction &action) {
sendAction(action);
const auto history = options.history;
const auto history = action.history;
const auto peer = history->peer;
const auto newId = FullMsgId(history->channelId(), clientMsgId());
const auto newId = FullMsgId(
history->channelId(),
session().data().nextLocalMessageId());
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
if (options.replyTo) {
auto clientFlags = NewMessageClientFlags();
if (action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
}
if (channelPost) {
@@ -4557,9 +4604,14 @@ void ApiWrap::sendSharedContact(
} else {
flags |= MTPDmessage::Flag::f_from_id;
}
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
const auto messageFromId = channelPost ? 0 : _session->userId();
const auto messagePostAuthor = channelPost
? App::peerName(_session->user())
? _session->user()->name
: QString();
const auto vcard = QString();
const auto views = 1;
@@ -4571,8 +4623,8 @@ void ApiWrap::sendSharedContact(
peerToMTP(peer->id),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(options.replyTo),
MTP_int(base::unixtime::now()),
MTP_int(action.replyTo),
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
MTP_string(),
MTP_messageMediaContact(
MTP_string(phone),
@@ -4585,7 +4637,10 @@ void ApiWrap::sendSharedContact(
MTP_int(views),
MTPint(),
MTP_string(messagePostAuthor),
MTPlong()),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
clientFlags,
NewMessageType::Unread);
const auto media = MTP_inputMediaContact(
@@ -4593,7 +4648,9 @@ void ApiWrap::sendSharedContact(
MTP_string(firstName),
MTP_string(lastName),
MTP_string(vcard));
sendMedia(item, media, _session->data().notifySilentPosts(peer));
auto options = action.options;
options.silent = _session->data().notifySilentPosts(peer);
sendMedia(item, media, options);
if (const auto main = App::main()) {
_session->data().sendHistoryChangeNotifications();
@@ -4606,9 +4663,9 @@ void ApiWrap::sendVoiceMessage(
QByteArray result,
VoiceWaveform waveform,
int duration,
const SendOptions &options) {
const SendAction &action) {
const auto caption = TextWithTags();
const auto to = fileLoadTaskOptions(options);
const auto to = fileLoadTaskOptions(action);
_fileLoader->addTask(std::make_unique<FileLoadTask>(
result,
duration,
@@ -4621,12 +4678,12 @@ void ApiWrap::editMedia(
Storage::PreparedList &&list,
SendMediaType type,
TextWithTags &&caption,
const SendOptions &options,
const SendAction &action,
MsgId msgIdToEdit) {
if (list.files.empty()) return;
auto &file = list.files.front();
const auto to = fileLoadTaskOptions(options);
const auto to = fileLoadTaskOptions(action);
_fileLoader->addTask(std::make_unique<FileLoadTask>(
file.path,
file.content,
@@ -4643,22 +4700,22 @@ void ApiWrap::sendFiles(
SendMediaType type,
TextWithTags &&caption,
std::shared_ptr<SendingAlbum> album,
const SendOptions &options) {
const SendAction &action) {
const auto haveCaption = !caption.text.isEmpty();
const auto isAlbum = (album != nullptr);
const auto compressImages = (type == SendMediaType::Photo);
if (haveCaption && !list.canAddCaption(isAlbum, compressImages)) {
auto message = MessageToSend(options.history);
auto message = MessageToSend(action.history);
message.textWithTags = std::move(caption);
message.replyTo = options.replyTo;
message.clearDraft = false;
message.action = action;
message.action.clearDraft = false;
sendMessage(std::move(message));
caption = TextWithTags();
}
const auto to = fileLoadTaskOptions(options);
const auto to = fileLoadTaskOptions(action);
if (album) {
album->silent = to.silent;
album->options = to.options;
}
auto tasks = std::vector<std::unique_ptr<Task>>();
tasks.reserve(list.files.size());
@@ -4697,8 +4754,8 @@ void ApiWrap::sendFiles(
void ApiWrap::sendFile(
const QByteArray &fileContent,
SendMediaType type,
const SendOptions &options) {
const auto to = fileLoadTaskOptions(options);
const SendAction &action) {
const auto to = fileLoadTaskOptions(action);
auto caption = TextWithTags();
_fileLoader->addTask(std::make_unique<FileLoadTask>(
QString(),
@@ -4712,7 +4769,7 @@ void ApiWrap::sendFile(
void ApiWrap::sendUploadedPhoto(
FullMsgId localId,
const MTPInputFile &file,
bool silent) {
Api::SendOptions options) {
if (const auto item = _session->data().message(localId)) {
const auto media = MTP_inputMediaUploadedPhoto(
MTP_flags(0),
@@ -4722,7 +4779,7 @@ void ApiWrap::sendUploadedPhoto(
if (const auto groupId = item->groupId()) {
uploadAlbumMedia(item, groupId, media);
} else {
sendMedia(item, media, silent);
sendMedia(item, media, options);
}
}
}
@@ -4731,7 +4788,7 @@ void ApiWrap::sendUploadedDocument(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
bool silent) {
Api::SendOptions options) {
if (const auto item = _session->data().message(localId)) {
auto media = item->media();
if (auto document = media ? media->document() : nullptr) {
@@ -4754,7 +4811,7 @@ void ApiWrap::sendUploadedDocument(
if (groupId) {
uploadAlbumMedia(item, groupId, media);
} else {
sendMedia(item, media, silent);
sendMedia(item, media, options);
}
}
}
@@ -4764,7 +4821,7 @@ void ApiWrap::editUploadedFile(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
bool silent,
Api::SendOptions options,
bool isDocument) {
const auto item = _session->data().message(localId);
if (!item) {
@@ -4774,9 +4831,9 @@ void ApiWrap::editUploadedFile(
return;
}
auto sentEntities = TextUtilities::EntitiesToMTP(
auto sentEntities = Api::EntitiesToMTP(
item->originalText().entities,
TextUtilities::ConvertOption::SkipLocal);
Api::ConvertOption::SkipLocal);
auto flagsEditMsg = MTPmessages_EditMessage::Flag::f_message | 0;
flagsEditMsg |= MTPmessages_EditMessage::Flag::f_no_webpage;
@@ -4829,7 +4886,8 @@ void ApiWrap::editUploadedFile(
MTP_string(item->originalText().text),
*media,
MTPReplyMarkup(),
sentEntities
sentEntities,
MTP_int(0) // schedule_date
)).done([=](const MTPUpdates &result) {
item->clearSavedMedia();
item->setIsLocalUpdateMedia(true);
@@ -4861,18 +4919,13 @@ void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
}
void ApiWrap::sendMessage(MessageToSend &&message) {
const auto history = message.history;
const auto history = message.action.history;
const auto peer = history->peer;
auto &textWithTags = message.textWithTags;
auto options = ApiWrap::SendOptions(history);
options.clearDraft = message.clearDraft;
options.replyTo = message.replyTo;
options.silent = message.silent;
options.generateLocal = true;
options.webPageId = message.webPageId;
options.handleSupportSwitch = message.handleSupportSwitch;
sendAction(options);
auto action = message.action;
action.generateLocal = true;
sendAction(action);
if (!peer->canWrite()) {
return;
@@ -4882,7 +4935,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
auto sending = TextWithEntities();
auto left = TextWithEntities {
textWithTags.text,
ConvertTextTagsToEntities(textWithTags.tags)
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
};
auto prepareFlags = Ui::ItemTextOptions(
history,
@@ -4892,7 +4945,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
HistoryItem *lastMessage = nullptr;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
auto newId = FullMsgId(
peerToChannel(peer->id),
session().data().nextLocalMessageId());
auto randomId = rand_value<uint64>();
TextUtilities::Trim(sending);
@@ -4902,8 +4957,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTPstring msgText(MTP_string(sending.text));
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_entities;
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendMessage::Flags(0);
if (message.replyTo) {
if (action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
}
@@ -4919,7 +4975,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
flags |= MTPDmessage::Flag::f_media;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = message.silent
const auto silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
@@ -4933,20 +4989,28 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (silentPost) {
sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
}
auto localEntities = TextUtilities::EntitiesToMTP(sending.entities);
auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal);
auto localEntities = Api::EntitiesToMTP(sending.entities);
auto sentEntities = Api::EntitiesToMTP(
sending.entities,
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
}
if (message.clearDraft) {
if (action.clearDraft) {
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
history->clearCloudDraft();
history->setSentDraftText(QString());
}
auto messageFromId = channelPost ? 0 : _session->userId();
auto messagePostAuthor = channelPost
? App::peerName(_session->user())
? _session->user()->name
: QString();
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
lastMessage = history->addNewMessage(
MTP_message(
MTP_flags(flags),
@@ -4955,8 +5019,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
peerToMTP(peer->id),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(message.replyTo),
MTP_int(base::unixtime::now()),
MTP_int(action.replyTo),
MTP_int(
HistoryItem::NewMessageDate(action.options.scheduled)),
msgText,
media,
MTPReplyMarkup(),
@@ -4964,16 +5029,20 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_int(1),
MTPint(),
MTP_string(messagePostAuthor),
MTPlong()),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
clientFlags,
NewMessageType::Unread);
history->sendRequestId = request(MTPmessages_SendMessage(
MTP_flags(sendFlags),
peer->input,
MTP_int(message.replyTo),
MTP_int(action.replyTo),
msgText,
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities
sentEntities,
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
@@ -4981,7 +5050,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (error.type() == qstr("MESSAGE_EMPTY")) {
lastMessage->destroy();
} else {
sendMessageFail(error, peer, newId);
sendMessageFail(error, peer, randomId, newId);
}
history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId
@@ -4989,7 +5058,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
}
if (const auto main = App::main()) {
main->finishForwarding(history, message.silent);
main->finishForwarding(action);
}
}
@@ -5028,22 +5097,25 @@ void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
void ApiWrap::sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendOptions &options) {
sendAction(options);
const SendAction &action) {
sendAction(action);
const auto history = options.history;
const auto history = action.history;
const auto peer = history->peer;
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
const auto newId = FullMsgId(
peerToChannel(peer->id),
session().data().nextLocalMessageId());
const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
if (options.replyTo) {
if (action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
}
bool channelPost = peer->isChannel() && !peer->isMegagroup();
bool silentPost = options.silent
bool silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
@@ -5060,25 +5132,29 @@ void ApiWrap::sendInlineResult(
if (bot) {
flags |= MTPDmessage::Flag::f_via_bot_id;
}
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
auto messageFromId = channelPost ? 0 : _session->userId();
auto messagePostAuthor = channelPost
? App::peerName(_session->user())
const auto messageFromId = channelPost ? 0 : _session->userId();
const auto messagePostAuthor = channelPost
? _session->user()->name
: QString();
MTPint messageDate = MTP_int(base::unixtime::now());
UserId messageViaBotId = bot ? peerToUser(bot->id) : 0;
MsgId messageId = newId.msg;
_session->data().registerMessageRandomId(randomId, newId);
data->addToHistory(
history,
flags,
messageId,
clientFlags,
newId.msg,
messageFromId,
messageDate,
messageViaBotId,
options.replyTo,
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
bot ? peerToUser(bot->id) : 0,
action.replyTo,
messagePostAuthor);
history->clearCloudDraft();
@@ -5087,21 +5163,22 @@ void ApiWrap::sendInlineResult(
history->sendRequestId = request(MTPmessages_SendInlineBotResult(
MTP_flags(sendFlags),
peer->input,
MTP_int(options.replyTo),
MTP_int(action.replyTo),
MTP_long(randomId),
MTP_long(data->getQueryId()),
MTP_string(data->getId())
MTP_string(data->getId()),
MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, newId);
sendMessageFail(error, peer, randomId, newId);
history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId
).send();
if (const auto main = App::main()) {
main->finishForwarding(history, options.silent);
main->finishForwarding(action);
}
}
@@ -5183,36 +5260,39 @@ void ApiWrap::uploadAlbumMedia(
void ApiWrap::sendMedia(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
bool silent) {
Api::SendOptions options) {
const auto randomId = rand_value<uint64>();
_session->data().registerMessageRandomId(randomId, item->fullId());
sendMediaWithRandomId(item, media, silent, randomId);
sendMediaWithRandomId(item, media, options, randomId);
}
void ApiWrap::sendMediaWithRandomId(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
bool silent,
Api::SendOptions options,
uint64 randomId) {
const auto history = item->history();
const auto replyTo = item->replyToId();
auto caption = item->originalText();
TextUtilities::Trim(caption);
auto sentEntities = TextUtilities::EntitiesToMTP(
auto sentEntities = Api::EntitiesToMTP(
caption.entities,
TextUtilities::ConvertOption::SkipLocal);
Api::ConvertOption::SkipLocal);
const auto flags = MTPmessages_SendMedia::Flags(0)
| (replyTo
? MTPmessages_SendMedia::Flag::f_reply_to_msg_id
: MTPmessages_SendMedia::Flag(0))
| (silent
| (options.silent
? MTPmessages_SendMedia::Flag::f_silent
: MTPmessages_SendMedia::Flag(0))
| (!sentEntities.v.isEmpty()
? MTPmessages_SendMedia::Flag::f_entities
: MTPmessages_SendMedia::Flag(0))
| (options.scheduled
? MTPmessages_SendMedia::Flag::f_schedule_date
: MTPmessages_SendMedia::Flag(0));
const auto peer = history->peer;
@@ -5225,11 +5305,12 @@ void ApiWrap::sendMediaWithRandomId(
MTP_string(caption.text),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities
sentEntities,
MTP_int(options.scheduled)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, itemId);
sendMessageFail(error, peer, randomId, itemId);
}).afterRequest(
history->sendRequestId
).send();
@@ -5293,7 +5374,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
sendMediaWithRandomId(
sample,
single.vmedia(),
album->silent,
album->options,
single.vrandom_id().v);
_sendingAlbums.remove(groupId);
return;
@@ -5304,22 +5385,26 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
| (replyTo
? MTPmessages_SendMultiMedia::Flag::f_reply_to_msg_id
: MTPmessages_SendMultiMedia::Flag(0))
| (album->silent
| (album->options.silent
? MTPmessages_SendMultiMedia::Flag::f_silent
: MTPmessages_SendMultiMedia::Flag(0))
| (album->options.scheduled
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
: MTPmessages_SendMultiMedia::Flag(0));
const auto peer = history->peer;
history->sendRequestId = request(MTPmessages_SendMultiMedia(
MTP_flags(flags),
peer->input,
MTP_int(replyTo),
MTP_vector<MTPInputSingleMedia>(medias)
MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(album->options.scheduled)
)).done([=](const MTPUpdates &result) {
_sendingAlbums.remove(groupId);
applyUpdates(result);
}).fail([=](const RPCError &error) {
if (const auto album = _sendingAlbums.take(groupId)) {
for (const auto &item : (*album)->items) {
sendMessageFail(error, peer, item.msgId);
sendMessageFail(error, peer, item.randomId, item.msgId);
}
} else {
sendMessageFail(error, peer);
@@ -5329,36 +5414,22 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
).send();
}
FileLoadTo ApiWrap::fileLoadTaskOptions(const SendOptions &options) const {
const auto peer = options.history->peer;
return FileLoadTo(
peer->id,
options.silent || _session->data().notifySilentPosts(peer),
options.replyTo);
}
void ApiWrap::requestSupportContact(FnMut<void(const MTPUser &)> callback) {
_supportContactCallbacks.push_back(std::move(callback));
if (_supportContactCallbacks.size() > 1) {
return;
FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
const auto peer = action.history->peer;
auto options = action.options;
if (_session->data().notifySilentPosts(peer)) {
options.silent = true;
}
request(MTPhelp_GetSupport(
)).done([=](const MTPhelp_Support &result) {
result.match([&](const MTPDhelp_support &data) {
for (auto &handler : base::take(_supportContactCallbacks)) {
handler(data.vuser());
}
});
}).fail([=](const RPCError &error) {
_supportContactCallbacks.clear();
}).send();
return FileLoadTo(peer->id, action.options, action.replyTo);
}
void ApiWrap::uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image) {
peer = peer->migrateToOrMe();
const auto ready = PreparePeerPhoto(peer->id, std::move(image));
const auto fakeId = FullMsgId(peerToChannel(peer->id), clientMsgId());
const auto fakeId = FullMsgId(
peerToChannel(peer->id),
session().data().nextLocalMessageId());
const auto already = ranges::find(
_peerPhotoUploads,
peer,
@@ -5766,30 +5837,33 @@ void ApiWrap::setSelfDestructDays(int days) {
void ApiWrap::createPoll(
const PollData &data,
const SendOptions &options,
const SendAction &action,
FnMut<void()> done,
FnMut<void(const RPCError &error)> fail) {
sendAction(options);
sendAction(action);
const auto history = options.history;
const auto history = action.history;
const auto peer = history->peer;
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (options.replyTo) {
if (action.replyTo) {
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
if (options.clearDraft) {
if (action.clearDraft) {
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
history->clearLocalDraft();
history->clearCloudDraft();
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = options.silent
const auto silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
if (action.options.scheduled) {
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
}
const auto replyTo = options.replyTo;
const auto replyTo = action.replyTo;
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
@@ -5798,7 +5872,8 @@ void ApiWrap::createPoll(
MTP_string(),
MTP_long(rand_value<uint64>()),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>()
MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled)
)).done([=, done = std::move(done)](const MTPUpdates &result) mutable {
applyUpdates(result);
done();
@@ -5874,7 +5949,8 @@ void ApiWrap::closePoll(not_null<HistoryItem*> item) {
MTPstring(),
MTP_inputMediaPoll(PollDataToMTP(poll)),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>()
MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date
)).done([=](const MTPUpdates &result) {
_pollCloseRequestIds.erase(itemId);
applyUpdates(result);

View File

@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <rpl/event_stream.h>
#include "api/api_common.h"
#include "base/timer.h"
#include "base/flat_map.h"
#include "base/flat_set.h"
@@ -65,15 +65,6 @@ inline QString ToString(uint64 value) {
} // namespace details
template <typename IntRange>
inline int32 CountHash(IntRange &&range) {
uint32 acc = 0;
for (auto value : range) {
acc += (acc * 20261) + uint32(value);
}
return int32(acc & 0x7FFFFFFF);
}
template <
typename ...Types,
typename = std::enable_if_t<(sizeof...(Types) > 0)>>
@@ -96,9 +87,13 @@ QString RequestKey(Types &&...values) {
class ApiWrap : public MTP::Sender, private base::Subscriber {
public:
using SendAction = Api::SendAction;
using MessageToSend = Api::MessageToSend;
struct Privacy {
enum class Key {
PhoneNumber,
AddedByPhone,
LastSeen,
Calls,
Invites,
@@ -375,31 +370,21 @@ public:
not_null<PeerData*> peer,
const std::vector<not_null<UserData*>> &users);
struct SendOptions {
SendOptions(not_null<History*> history);
not_null<History*> history;
MsgId replyTo = 0;
WebPageId webPageId = 0;
bool silent = false;
bool clearDraft = false;
bool generateLocal = true;
bool handleSupportSwitch = false;
};
rpl::producer<SendOptions> sendActions() const {
rpl::producer<SendAction> sendActions() const {
return _sendActions.events();
}
void sendAction(const SendOptions &options);
void sendAction(const SendAction &action);
void forwardMessages(
HistoryItemsList &&items,
const SendOptions &options,
const SendAction &action,
FnMut<void()> &&successCallback = nullptr);
void shareContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
const SendOptions &options);
void shareContact(not_null<UserData*> user, const SendOptions &options);
const SendAction &action);
void shareContact(not_null<UserData*> user, const SendAction &action);
void readServerHistory(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history);
//void readFeed( // #feed
@@ -410,67 +395,55 @@ public:
QByteArray result,
VoiceWaveform waveform,
int duration,
const SendOptions &options);
const SendAction &action);
void sendFiles(
Storage::PreparedList &&list,
SendMediaType type,
TextWithTags &&caption,
std::shared_ptr<SendingAlbum> album,
const SendOptions &options);
const SendAction &action);
void sendFile(
const QByteArray &fileContent,
SendMediaType type,
const SendOptions &options);
const SendAction &action);
void editMedia(
Storage::PreparedList &&list,
SendMediaType type,
TextWithTags &&caption,
const SendOptions &options,
const SendAction &action,
MsgId msgIdToEdit);
void sendUploadedPhoto(
FullMsgId localId,
const MTPInputFile &file,
bool silent);
Api::SendOptions options);
void sendUploadedDocument(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
bool silent);
Api::SendOptions options);
void editUploadedFile(
FullMsgId localId,
const MTPInputFile &file,
const std::optional<MTPInputFile> &thumb,
bool silent,
Api::SendOptions options,
bool isDocument);
void cancelLocalItem(not_null<HistoryItem*> item);
struct MessageToSend {
MessageToSend(not_null<History*> history);
not_null<History*> history;
TextWithTags textWithTags;
MsgId replyTo = 0;
WebPageId webPageId = 0;
bool silent = false;
bool clearDraft = true;
bool handleSupportSwitch = false;
};
void sendMessage(MessageToSend &&message);
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
void sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendOptions &options);
const SendAction &action);
void sendMessageFail(
const RPCError &error,
not_null<PeerData*> peer,
uint64 randomId = 0,
FullMsgId itemId = FullMsgId());
void requestSupportContact(FnMut<void(const MTPUser&)> callback);
void uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image);
void clearPeerPhoto(not_null<PhotoData*> photo);
@@ -498,7 +471,7 @@ public:
void createPoll(
const PollData &data,
const SendOptions &options,
const SendAction &action,
FnMut<void()> done,
FnMut<void(const RPCError &error)> fail);
void sendPollVotes(
@@ -643,7 +616,7 @@ private:
const QString &firstName,
const QString &lastName,
UserId userId,
const SendOptions &options);
const SendAction &action);
void deleteHistory(
not_null<PeerData*> peer,
@@ -677,13 +650,13 @@ private:
void sendMedia(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
bool silent);
Api::SendOptions options);
void sendMediaWithRandomId(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
bool silent,
Api::SendOptions options,
uint64 randomId);
FileLoadTo fileLoadTaskOptions(const SendOptions &options) const;
FileLoadTo fileLoadTaskOptions(const SendAction &action) const;
//void readFeeds(); // #feed
@@ -826,7 +799,7 @@ private:
not_null<Data::Folder*>,
DialogsLoadState> _foldersLoadState;
rpl::event_stream<SendOptions> _sendActions;
rpl::event_stream<SendAction> _sendActions;
struct ReadRequest {
ReadRequest(mtpRequestId requestId, MsgId upTo)

View File

@@ -47,6 +47,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include <QtCore/QBuffer>
#include <QtGui/QFontDatabase>
#ifdef OS_MAC_OLD
#include <libexif/exif-data.h>
#endif // OS_MAC_OLD
@@ -60,8 +63,6 @@ namespace {
*pressedLinkItem = nullptr,
*mousedItem = nullptr;
style::font monofont;
struct CornersPixmaps {
QPixmap p[4];
};
@@ -102,23 +103,6 @@ namespace App {
return result;
}
MainWindow *wnd() {
return (Core::IsAppLaunched() && Core::App().activeWindow())
? Core::App().activeWindow()->widget().get()
: nullptr;
}
MainWidget *main() {
if (auto window = wnd()) {
return window->mainWidget();
}
return nullptr;
}
QString peerName(const PeerData *peer, bool forDialogs) {
return peer ? ((forDialogs && peer->isUser() && !peer->asUser()->nameOrPhone.isEmpty()) ? peer->asUser()->nameOrPhone : peer->name) : tr::lng_deleted(tr::now);
}
void prepareCorners(RoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) {
Expects(::corners.size() > index);
@@ -152,14 +136,6 @@ namespace App {
}
}
void tryFontFamily(QString &family, const QString &tryFamily) {
if (family.isEmpty()) {
if (!QFontInfo(QFont(tryFamily)).family().trimmed().compare(tryFamily, Qt::CaseInsensitive)) {
family = tryFamily;
}
}
}
void createMaskCorners() {
QImage mask[4];
prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask);
@@ -218,16 +194,6 @@ namespace App {
}
void initMedia() {
if (!::monofont) {
QString family;
tryFontFamily(family, qsl("Consolas"));
tryFontFamily(family, qsl("Liberation Mono"));
tryFontFamily(family, qsl("Menlo"));
tryFontFamily(family, qsl("Courier"));
if (family.isEmpty()) family = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();
::monofont = style::font(st::normalFont->f.pixelSize(), 0, family);
}
createCorners();
using Update = Window::Theme::BackgroundUpdate;
@@ -307,10 +273,6 @@ namespace App {
mousedItem(nullptr);
}
const style::font &monofont() {
return ::monofont;
}
void quit() {
if (quitting()) {
return;
@@ -465,15 +427,6 @@ namespace App {
rectWithCorners(p, rect, st::msgInBg, MessageInCorners, corners);
}
QImage *cornersMask(ImageRoundRadius radius) {
switch (radius) {
case ImageRoundRadius::Large: return ::cornersMaskLarge;
case ImageRoundRadius::Small:
default: break;
}
return ::cornersMaskSmall;
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) {
auto cornerWidth = corner.p[0].width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0].height() / cIntRetinaFactor();

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "data/data_types.h"
#include "ui/rect_part.h"
enum class ImageRoundRadius;
class MainWindow;
@@ -18,10 +19,16 @@ namespace HistoryView {
class Element;
} // namespace HistoryView
namespace Media {
namespace Clip {
class Reader;
} // namespace Clip
} // namespace Media
using HistoryItemsMap = base::flat_set<not_null<HistoryItem*>>;
using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
enum RoundCorners {
enum RoundCorners : int {
SmallMaskCorners = 0x00, // for images
LargeMaskCorners,
@@ -60,13 +67,8 @@ enum RoundCorners {
};
namespace App {
MainWindow *wnd();
MainWidget *main();
QString formatPhone(QString phone);
[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
void hoveredItem(HistoryView::Element *item);
HistoryView::Element *hoveredItem();
void pressedItem(HistoryView::Element *item);
@@ -79,8 +81,6 @@ namespace App {
HistoryView::Element *mousedItem();
void clearMousedItems();
const style::font &monofont();
void initMedia();
void deinitMedia();
@@ -104,7 +104,6 @@ namespace App {
void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
QImage *cornersMask(ImageRoundRadius radius);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full);
inline void roundRect(Painter &p, const QRect &rect, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts);

View File

@@ -32,6 +32,17 @@ inline bool contains(const Container &container, const T &value) {
return std::find(std::begin(container), end, value) != end;
}
template <typename D, typename T>
inline constexpr D up_cast(T object) {
using DV = std::decay_t<decltype(*D())>;
using TV = std::decay_t<decltype(*T())>;
if constexpr (std::is_base_of_v<DV, TV>) {
return object;
} else {
return nullptr;
}
}
// We need a custom comparator for set<std::unique_ptr<T>>::find to work with pointers.
// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs
template <typename T>
@@ -70,6 +81,23 @@ struct pointer_comparator {
};
inline QString FromUtf8Safe(const char *string, int size = -1) {
if (!string || !size) {
return QString();
} else if (size < 0) {
size = strlen(string);
}
const auto result = QString::fromUtf8(string, size);
const auto back = result.toUtf8();
return (back.size() != size || memcmp(back.constData(), string, size))
? QString::fromLocal8Bit(string, size)
: result;
}
inline QString FromUtf8Safe(const QByteArray &string) {
return FromUtf8Safe(string.constData(), string.size());
}
} // namespace base
template <typename T>

View File

@@ -9,6 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <cstdlib>
// Ensures/Expects.
#include <gsl/gsl_assert>
namespace base {
namespace assertion {

View File

@@ -0,0 +1,18 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
// Methods that must be implemented outside lib_base.
namespace base {
void EnterFromEventLoop(FnMut<void()> &&method);
} // namespace

View File

@@ -7,3 +7,4 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/base_pch.h"
// Precompiled header helper.

View File

@@ -0,0 +1,61 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/crc32hash.h"
namespace base {
namespace {
class Crc32Table {
public:
Crc32Table() {
auto poly = std::uint32_t(0x04c11db7);
for (auto i = 0; i != 256; ++i) {
_data[i] = reflect(i, 8) << 24;
for (auto j = 0; j != 8; ++j) {
_data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0);
}
_data[i] = reflect(_data[i], 32);
}
}
std::uint32_t operator[](int index) const {
return _data[index];
}
private:
std::uint32_t reflect(std::uint32_t val, char ch) {
auto result = std::uint32_t(0);
for (int i = 1; i < (ch + 1); ++i) {
if (val & 1) {
result |= 1 << (ch - i);
}
val >>= 1;
}
return result;
}
std::uint32_t _data[256];
};
} // namespace
std::int32_t crc32(const void *data, int len) {
static const auto kTable = Crc32Table();
const auto buffer = static_cast<const std::uint8_t*>(data);
auto crc = std::uint32_t(0xffffffff);
for (auto i = 0; i != len; ++i) {
crc = (crc >> 8) ^ kTable[(crc & 0xFF) ^ buffer[i]];
}
return static_cast<std::int32_t>(crc ^ 0xffffffff);
}
} // namespace base

View File

@@ -0,0 +1,16 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <cstdint>
namespace base {
std::int32_t crc32(const void *data, int len);
} // namespace base

View File

@@ -40,3 +40,23 @@ inline void InvokeQueued(const QObject *context, Lambda &&lambda) {
const_cast<QObject*>(context),
new base::InvokeQueuedEvent(std::forward<Lambda>(lambda)));
}
class SingleQueuedInvokation : public QObject {
public:
SingleQueuedInvokation(Fn<void()> callback) : _callback(callback) {
}
void call() {
if (_pending.testAndSetAcquire(0, 1)) {
InvokeQueued(this, [this] {
if (_pending.testAndSetRelease(1, 0)) {
_callback();
}
});
}
}
private:
Fn<void()> _callback;
QAtomicInt _pending = { 0 };
};

View File

@@ -0,0 +1,121 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QPointer>
// Smart pointer for QObject*, has move semantics, destroys object if it doesn't have a parent.
template <typename Object>
class object_ptr {
public:
object_ptr(std::nullptr_t) noexcept {
}
// No default constructor, but constructors with at least
// one argument are simply make functions.
template <typename Parent, typename... Args>
explicit object_ptr(Parent &&parent, Args&&... args)
: _object(new Object(std::forward<Parent>(parent), std::forward<Args>(args)...)) {
}
static object_ptr<Object> fromRaw(Object *value) noexcept {
object_ptr<Object> result = { nullptr };
result._object = value;
return result;
}
Object *release() noexcept {
return static_cast<Object*>(base::take(_object).data());
}
object_ptr(const object_ptr &other) = delete;
object_ptr &operator=(const object_ptr &other) = delete;
object_ptr(object_ptr &&other) noexcept : _object(base::take(other._object)) {
}
object_ptr &operator=(object_ptr &&other) noexcept {
auto temp = std::move(other);
destroy();
std::swap(_object, temp._object);
return *this;
}
template <
typename OtherObject,
typename = std::enable_if_t<
std::is_base_of_v<Object, OtherObject>>>
object_ptr(object_ptr<OtherObject> &&other) noexcept
: _object(base::take(other._object)) {
}
template <
typename OtherObject,
typename = std::enable_if_t<
std::is_base_of_v<Object, OtherObject>>>
object_ptr &operator=(object_ptr<OtherObject> &&other) noexcept {
_object = base::take(other._object);
return *this;
}
object_ptr &operator=(std::nullptr_t) noexcept {
_object = nullptr;
return *this;
}
// So we can pass this pointer to methods like connect().
Object *data() const noexcept {
return static_cast<Object*>(_object.data());
}
operator Object*() const noexcept {
return data();
}
explicit operator bool() const noexcept {
return _object != nullptr;
}
Object *operator->() const noexcept {
return data();
}
Object &operator*() const noexcept {
return *data();
}
// Use that instead "= new Object(parent, ...)"
template <typename Parent, typename... Args>
Object *create(Parent &&parent, Args&&... args) {
destroy();
_object = new Object(
std::forward<Parent>(parent),
std::forward<Args>(args)...);
return data();
}
void destroy() noexcept {
delete base::take(_object);
}
void destroyDelayed() {
if (_object) {
if (auto widget = base::up_cast<QWidget*>(data())) {
widget->hide();
}
base::take(_object)->deleteLater();
}
}
~object_ptr() noexcept {
if (auto pointer = _object) {
if (!pointer->parent()) {
destroy();
}
}
}
private:
template <typename OtherObject>
friend class object_ptr;
QPointer<QObject> _object;
};

View File

@@ -57,19 +57,38 @@ private:
class BigNum {
public:
BigNum() : _data(BN_new()) {
BigNum() = default;
BigNum(const BigNum &other)
: _data((other.failed() || other.isZero())
? nullptr
: BN_dup(other.raw()))
, _failed(other._failed) {
}
BigNum(const BigNum &other) : BigNum() {
*this = other;
BigNum(BigNum &&other)
: _data(std::exchange(other._data, nullptr))
, _failed(std::exchange(other._failed, false)) {
}
BigNum &operator=(const BigNum &other) {
if (other.failed() || !BN_copy(raw(), other.raw())) {
if (other.failed()) {
_failed = true;
} else if (other.isZero()) {
clear();
_failed = false;
} else if (!_data) {
_data = BN_dup(other.raw());
_failed = false;
} else {
_failed = !BN_copy(raw(), other.raw());
}
return *this;
}
BigNum &operator=(BigNum &&other) {
std::swap(_data, other._data);
std::swap(_failed, other._failed);
return *this;
}
~BigNum() {
BN_clear_free(raw());
clear();
}
explicit BigNum(unsigned int word) : BigNum() {
@@ -79,64 +98,74 @@ public:
setBytes(bytes);
}
void setWord(unsigned int word) {
if (!BN_set_word(raw(), word)) {
_failed = true;
BigNum &setWord(unsigned int word) {
if (!word) {
clear();
_failed = false;
} else {
_failed = !BN_set_word(raw(), word);
}
return *this;
}
void setBytes(bytes::const_span bytes) {
if (!BN_bin2bn(
BigNum &setBytes(bytes::const_span bytes) {
if (bytes.empty()) {
clear();
_failed = false;
} else {
_failed = !BN_bin2bn(
reinterpret_cast<const unsigned char*>(bytes.data()),
bytes.size(),
raw())) {
_failed = true;
raw());
}
return *this;
}
void setAdd(const BigNum &a, const BigNum &b) {
BigNum &setAdd(const BigNum &a, const BigNum &b) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_add(raw(), a.raw(), b.raw())) {
_failed = true;
} else {
_failed = !BN_add(raw(), a.raw(), b.raw());
}
return *this;
}
void setSub(const BigNum &a, const BigNum &b) {
BigNum &setSub(const BigNum &a, const BigNum &b) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_sub(raw(), a.raw(), b.raw())) {
_failed = true;
} else {
_failed = !BN_sub(raw(), a.raw(), b.raw());
}
return *this;
}
void setSubWord(unsigned int word) {
if (failed()) {
return;
} else if (!BN_sub_word(raw(), word)) {
_failed = true;
}
}
void setMul(
BigNum &setMul(
const BigNum &a,
const BigNum &b,
const Context &context = Context()) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_mul(raw(), a.raw(), b.raw(), context.raw())) {
_failed = true;
} else {
_failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw());
}
return *this;
}
BN_ULONG setDivWord(BN_ULONG word) {
Expects(word != 0);
if (failed()) {
return (BN_ULONG)-1;
}
auto result = BN_div_word(raw(), word);
if (result == (BN_ULONG)-1) {
BigNum &setModAdd(
const BigNum &a,
const BigNum &b,
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || b.failed() || m.failed()) {
_failed = true;
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return result;
return *this;
}
void setModSub(
BigNum &setModSub(
const BigNum &a,
const BigNum &b,
const BigNum &m,
@@ -149,9 +178,12 @@ public:
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
void setModMul(
BigNum &setModMul(
const BigNum &a,
const BigNum &b,
const BigNum &m,
@@ -164,9 +196,29 @@ public:
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
void setModExp(
BigNum &setModInverse(
const BigNum &a,
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || m.failed()) {
_failed = true;
} else if (a.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
BigNum &setModExp(
const BigNum &base,
const BigNum &power,
const BigNum &m,
@@ -179,23 +231,34 @@ public:
_failed = true;
} else if (isNegative()) {
_failed = true;
} else {
_failed = false;
}
return *this;
}
bool isNegative() const {
return failed() ? false : BN_is_negative(raw());
[[nodiscard]] bool isZero() const {
return !failed() && (!_data || BN_is_zero(raw()));
}
bool isPrime(const Context &context = Context()) const {
if (failed()) {
[[nodiscard]] bool isOne() const {
return !failed() && _data && BN_is_one(raw());
}
[[nodiscard]] bool isNegative() const {
return !failed() && _data && BN_is_negative(raw());
}
[[nodiscard]] bool isPrime(const Context &context = Context()) const {
if (failed() || !_data) {
return false;
}
constexpr auto kMillerRabinIterationCount = 30;
auto result = BN_is_prime_ex(
const auto result = BN_is_prime_ex(
raw(),
kMillerRabinIterationCount,
context.raw(),
NULL);
nullptr);
if (result == 1) {
return true;
} else if (result != 0) {
@@ -204,27 +267,42 @@ public:
return false;
}
BN_ULONG modWord(BN_ULONG word) const {
Expects(word != 0);
BigNum &subWord(unsigned int word) {
if (failed()) {
return (BN_ULONG)-1;
return *this;
} else if (!BN_sub_word(raw(), word)) {
_failed = true;
}
return *this;
}
BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) {
Expects(word != 0);
auto result = BN_mod_word(raw(), word);
const auto result = failed()
? (BN_ULONG)-1
: BN_div_word(raw(), word);
if (result == (BN_ULONG)-1) {
_failed = true;
}
return result;
if (mod) {
*mod = result;
}
return *this;
}
[[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const {
Expects(word != 0);
return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word);
}
int bitsSize() const {
[[nodiscard]] int bitsSize() const {
return failed() ? 0 : BN_num_bits(raw());
}
int bytesSize() const {
[[nodiscard]] int bytesSize() const {
return failed() ? 0 : BN_num_bytes(raw());
}
bytes::vector getBytes() const {
[[nodiscard]] bytes::vector getBytes() const {
if (failed()) {
return {};
}
@@ -237,73 +315,84 @@ public:
return result;
}
BIGNUM *raw() {
[[nodiscard]] BIGNUM *raw() {
if (!_data) _data = BN_new();
return _data;
}
const BIGNUM *raw() const {
[[nodiscard]] const BIGNUM *raw() const {
if (!_data) _data = BN_new();
return _data;
}
BIGNUM *takeRaw() {
return base::take(_data);
[[nodiscard]] BIGNUM *takeRaw() {
return _failed
? nullptr
: _data
? std::exchange(_data, nullptr)
: BN_new();
}
bool failed() const {
[[nodiscard]] bool failed() const {
return _failed;
}
static BigNum Add(const BigNum &a, const BigNum &b) {
BigNum result;
result.setAdd(a, b);
return result;
[[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) {
return BigNum().setAdd(a, b);
}
static BigNum Sub(const BigNum &a, const BigNum &b) {
BigNum result;
result.setSub(a, b);
return result;
[[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) {
return BigNum().setSub(a, b);
}
static BigNum Mul(
[[nodiscard]] static BigNum Mul(
const BigNum &a,
const BigNum &b,
const Context &context = Context()) {
BigNum result;
result.setMul(a, b, context);
return result;
return BigNum().setMul(a, b, context);
}
static BigNum ModSub(
[[nodiscard]] static BigNum ModAdd(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModSub(a, b, mod, context);
return result;
return BigNum().setModAdd(a, b, mod, context);
}
static BigNum ModMul(
[[nodiscard]] static BigNum ModSub(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModMul(a, b, mod, context);
return result;
return BigNum().setModSub(a, b, mod, context);
}
static BigNum ModExp(
[[nodiscard]] static BigNum ModMul(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
return BigNum().setModMul(a, b, mod, context);
}
[[nodiscard]] static BigNum ModInverse(
const BigNum &a,
const BigNum &mod,
const Context &context = Context()) {
return BigNum().setModInverse(a, mod, context);
}
[[nodiscard]] static BigNum ModExp(
const BigNum &base,
const BigNum &power,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModExp(base, power, mod, context);
return result;
return BigNum().setModExp(base, power, mod, context);
}
static BigNum Failed() {
BigNum result;
[[nodiscard]] static BigNum Failed() {
auto result = BigNum();
result._failed = true;
return result;
}
private:
BIGNUM *_data = nullptr;
void clear() {
BN_clear_free(std::exchange(_data, nullptr));
}
mutable BIGNUM *_data = nullptr;
mutable bool _failed = false;
};
@@ -435,6 +524,17 @@ inline void AddRandomSeed(bytes::const_span data) {
RAND_seed(data.data(), data.size());
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
[[nodiscard]] inline T RandomValue() {
unsigned char buffer[sizeof(T)];
if (!RAND_bytes(buffer, sizeof(T))) {
Unexpected("Could not generate random bytes!");
}
auto result = T();
memcpy(&result, buffer, sizeof(T));
return result;
}
inline bytes::vector Pbkdf2Sha512(
bytes::const_span password,
bytes::const_span salt,

View File

@@ -0,0 +1,49 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/algorithm.h"
#include <QtCore/QObject>
namespace base {
class qt_connection final {
public:
qt_connection(QMetaObject::Connection data = {}) : _data(data) {
}
qt_connection(qt_connection &&other) : _data(base::take(other._data)) {
}
qt_connection &operator=(qt_connection &&other) {
reset(base::take(other._data));
return *this;
}
~qt_connection() {
disconnect();
}
void release() {
_data = QMetaObject::Connection();
}
void reset(QMetaObject::Connection data = {}) {
disconnect();
_data = data;
}
private:
void disconnect() {
if (_data) {
QObject::disconnect(base::take(_data));
}
}
QMetaObject::Connection _data;
};
} // namespace base

View File

@@ -7,9 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/sandbox.h"
#include "base/base_integration.h"
namespace Core {
namespace base {
// This method allows to create an rpl::producer from a Qt object
// and a signal with none or one reported value.
@@ -20,34 +20,34 @@ namespace Core {
// This means that all postponeCall's will be invoked right after
// the value processing by the current consumer finishes.
template <typename Object, typename Signal>
auto QtSignalProducer(Object *object, Signal signal);
auto qt_signal_producer(Object *object, Signal signal);
namespace details {
template <typename Signal>
struct QtSignalArgument;
struct qt_signal_argument;
template <typename Class, typename Return, typename Value>
struct QtSignalArgument<Return(Class::*)(Value)> {
struct qt_signal_argument<Return(Class::*)(Value)> {
using type = Value;
};
template <typename Class, typename Return>
struct QtSignalArgument<Return(Class::*)()> {
struct qt_signal_argument<Return(Class::*)()> {
using type = void;
};
} // namespace details
template <typename Object, typename Signal>
auto QtSignalProducer(Object *object, Signal signal) {
using Value = typename details::QtSignalArgument<Signal>::type;
auto qt_signal_producer(Object *object, Signal signal) {
using Value = typename details::qt_signal_argument<Signal>::type;
static constexpr auto NoArgument = std::is_same_v<Value, void>;
using Produced = std::conditional_t<
NoArgument,
rpl::empty_value,
std::remove_const_t<std::decay_t<Value>>>;
const auto guarded = make_weak(object);
const auto guarded = QPointer<Object>(object);
return rpl::make_producer<Produced>([=](auto consumer) {
if (!guarded) {
return rpl::lifetime();
@@ -59,7 +59,7 @@ auto QtSignalProducer(Object *object, Signal signal) {
signal,
listener,
std::forward<decltype(handler)>(handler));
const auto weak = make_weak(listener);
const auto weak = QPointer<QObject>(listener);
return rpl::lifetime([=] {
if (weak) {
delete weak;
@@ -67,7 +67,7 @@ auto QtSignalProducer(Object *object, Signal signal) {
});
};
auto put = [=](const Produced &value) {
Sandbox::Instance().customEnterFromEventLoop([&] {
EnterFromEventLoop([&] {
consumer.put_next_copy(value);
});
};
@@ -79,4 +79,4 @@ auto QtSignalProducer(Object *object, Signal signal) {
});
}
} // namespace Core
} // namespace base

View File

@@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QUrl>
#include <QtCore/QString>
#include <QtCore/QRegularExpression>
namespace qthelp {
const QRegularExpression &RegExpDomain();

View File

@@ -108,8 +108,8 @@ void Timer::timerEvent(QTimerEvent *e) {
cancel();
}
if (_callback) {
_callback();
if (const auto onstack = _callback) {
onstack();
}
}

View File

@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QPointer>
namespace base {
template <typename T>

View File

@@ -7,12 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/unixtime.h"
#include "logs.h"
#include <QDateTime>
#include <QReadWriteLock>
#ifdef Q_OS_WIN
#include <windows.h>
#elif defined Q_OS_MAC
#include <mach/mach_time.h>
#else
@@ -124,19 +123,15 @@ TimeId now() {
void update(TimeId now, bool force) {
if (force) {
DEBUG_LOG(("MTP Info: forcing client unixtime to %1"
).arg(now));
ValueUpdated = true;
} else {
auto expected = false;
if (!ValueUpdated.compare_exchange_strong(expected, true)) {
return;
}
DEBUG_LOG(("MTP Info: setting client unixtime to %1").arg(now));
}
const auto shift = now + 1 - local();
ValueShift = shift;
DEBUG_LOG(("MTP Info: now unixtimeDelta is %1").arg(shift));
HttpValueShift = 0;
HttpValueValid = false;

View File

@@ -20,6 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/update_checker.h"
#include "styles/style_boxes.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
#include <QtGui/QDesktopServices>
namespace {
rpl::producer<TextWithEntities> Text1() {
@@ -100,7 +104,7 @@ void AboutBox::showVersionHistory() {
}
url = url.arg(qsl("talpha%1_%2").arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
QApplication::clipboard()->setText(url);
QGuiApplication::clipboard()->setText(url);
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
} else {

View File

@@ -11,14 +11,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_profile.h"
#include "storage/localstorage.h"
#include "lang/lang_keys.h"
#include "ui/effects/radial_animation.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
#include "base/timer.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "app.h"
struct AbstractBox::LoadingProgress {
LoadingProgress(
Fn<void()> &&callback,
const style::InfiniteRadialAnimation &st);
Ui::InfiniteRadialAnimation animation;
base::Timer removeTimer;
};
AbstractBox::LoadingProgress::LoadingProgress(
Fn<void()> &&callback,
const style::InfiniteRadialAnimation &st)
: animation(std::move(callback), st) {
}
void BoxContent::setTitle(rpl::producer<QString> title) {
getDelegate()->setTitle(std::move(title) | Ui::Text::ToWithEntities());
@@ -117,7 +136,7 @@ void BoxContent::onDraggingScrollDelta(int delta) {
}
void BoxContent::onDraggingScrollTimer() {
auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed));
auto delta = (_draggingScrollDelta > 0) ? qMin(_draggingScrollDelta * 3 / 20 + 1, int32(Ui::kMaxScrollSpeed)) : qMax(_draggingScrollDelta * 3 / 20 - 1, -int32(Ui::kMaxScrollSpeed));
_scroll->scrollToY(_scroll->scrollTop() + delta);
}
@@ -258,7 +277,6 @@ AbstractBox::AbstractBox(
: LayerWidget(layer)
, _layer(layer)
, _content(std::move(content)) {
subscribe(Lang::Current().updated(), [=] { refreshLang(); });
_content->setParent(this);
_content->setDelegate(this);
@@ -269,6 +287,8 @@ AbstractBox::AbstractBox(
}, lifetime());
}
AbstractBox::~AbstractBox() = default;
void AbstractBox::setLayerType(bool layerType) {
_layerType = layerType;
updateTitlePosition();
@@ -279,15 +299,35 @@ int AbstractBox::titleHeight() const {
}
int AbstractBox::buttonsHeight() const {
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
const auto padding = _layerType
? st::boxLayerButtonPadding
: st::boxButtonPadding;
return padding.top() + st::defaultBoxButton.height + padding.bottom();
}
int AbstractBox::buttonsTop() const {
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
const auto padding = _layerType
? st::boxLayerButtonPadding
: st::boxButtonPadding;
return height() - padding.bottom() - st::defaultBoxButton.height;
}
QRect AbstractBox::loadingRect() const {
const auto padding = _layerType
? st::boxLayerButtonPadding
: st::boxButtonPadding;
const auto size = st::boxLoadingSize;
const auto skipx = _layerType
? st::boxLayerTitlePosition.x()
: st::boxTitlePosition.x();
const auto skipy = (st::defaultBoxButton.height - size) / 2;
return QRect(
skipx,
height() - padding.bottom() - skipy - size,
size,
size);
}
void AbstractBox::paintEvent(QPaintEvent *e) {
Painter p(this);
auto clip = e->rect();
@@ -309,6 +349,14 @@ void AbstractBox::paintEvent(QPaintEvent *e) {
&& clip.intersects(QRect(0, 0, width(), titleHeight()))) {
paintAdditionalTitle(p);
}
if (_loadingProgress) {
const auto rect = loadingRect();
_loadingProgress->animation.draw(
p,
rect.topLeft(),
rect.size(),
width());
}
}
void AbstractBox::paintAdditionalTitle(Painter &p) {
@@ -350,10 +398,6 @@ bool AbstractBox::closeByOutsideClick() const {
return _closeByOutsideClick;
}
void AbstractBox::refreshLang() {
InvokeQueued(this, [this] { updateButtonsPositions(); });
}
bool AbstractBox::hasTitle() const {
return (_title != nullptr) || !_additionalTitle.current().isEmpty();
}
@@ -416,7 +460,10 @@ QPointer<Ui::RoundButton> AbstractBox::addButton(
auto result = QPointer<Ui::RoundButton>(_buttons.back());
result->setClickedCallback(std::move(clickCallback));
result->show();
updateButtonsPositions();
result->widthValue(
) | rpl::start_with_next([=] {
updateButtonsPositions();
}, result->lifetime());
return result;
}
@@ -428,7 +475,10 @@ QPointer<Ui::RoundButton> AbstractBox::addLeftButton(
auto result = QPointer<Ui::RoundButton>(_leftButton);
result->setClickedCallback(std::move(clickCallback));
result->show();
updateButtonsPositions();
result->widthValue(
) | rpl::start_with_next([=] {
updateButtonsPositions();
}, result->lifetime());
return result;
}
@@ -441,6 +491,36 @@ QPointer<Ui::IconButton> AbstractBox::addTopButton(const style::IconButton &st,
return result;
}
void AbstractBox::showLoading(bool show) {
const auto &st = st::boxLoadingAnimation;
if (!show) {
if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) {
_loadingProgress->removeTimer.callOnce(
st.sineDuration + st.sinePeriod);
_loadingProgress->animation.stop();
}
return;
}
if (!_loadingProgress) {
const auto callback = [=] {
if (!anim::Disabled()) {
const auto t = st::boxLoadingAnimation.thickness;
update(loadingRect().marginsAdded({ t, t, t, t }));
}
};
_loadingProgress = std::make_unique<LoadingProgress>(
callback,
st::boxLoadingAnimation);
_loadingProgress->removeTimer.setCallback([=] {
_loadingProgress = nullptr;
});
} else {
_loadingProgress->removeTimer.cancel();
}
_loadingProgress->animation.start();
}
void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) {
_maxContentHeight = maxHeight;
@@ -519,3 +599,39 @@ void BoxContentDivider::paintEvent(QPaintEvent *e) {
auto dividerFillBottom = myrtlrect(0, height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height());
st::profileDividerBottom.fill(p, dividerFillBottom);
}
namespace Ui {
namespace internal {
void showBox(
object_ptr<BoxContent> content,
LayerOptions options,
anim::type animated) {
if (auto w = App::wnd()) {
w->ui_showBox(std::move(content), options, animated);
}
}
} // namespace internal
void hideLayer(anim::type animated) {
if (auto w = App::wnd()) {
w->ui_showBox(
{ nullptr },
LayerOption::CloseOther,
animated);
}
}
void hideSettingsAndLayer(anim::type animated) {
if (auto w = App::wnd()) {
w->ui_hideSettingsAndLayer(animated);
}
}
bool isLayerShown() {
if (auto w = App::wnd()) return w->ui_isLayerShown();
return false;
}
} // namespace Ui

View File

@@ -9,8 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/layer_widget.h"
#include "base/unique_qptr.h"
#include "base/flags.h"
#include "ui/effects/animation_value.h"
#include "ui/text/text_entity.h"
#include "ui/rp_widget.h"
class Painter;
namespace style {
struct RoundButton;
struct IconButton;
@@ -46,6 +51,7 @@ public:
virtual QPointer<Ui::IconButton> addTopButton(
const style::IconButton &st,
Fn<void()> clickCallback) = 0;
virtual void showLoading(bool show) = 0;
virtual void updateButtonsPositions() = 0;
virtual void showBox(
@@ -74,7 +80,7 @@ public:
};
class BoxContent : public Ui::RpWidget, protected base::Subscriber {
class BoxContent : public Ui::RpWidget {
Q_OBJECT
public:
@@ -133,6 +139,9 @@ public:
std::move(clickCallback),
st);
}
void showLoading(bool show) {
getDelegate()->showLoading(show);
}
void updateButtonsGeometry() {
getDelegate()->updateButtonsPositions();
}
@@ -219,7 +228,8 @@ protected:
template <typename Widget>
object_ptr<Widget> takeInnerWidget() {
return static_object_cast<Widget>(doTakeInnerWidget());
return object_ptr<Widget>::fromRaw(
static_cast<Widget*>(doTakeInnerWidget().release()));
}
void setInnerVisible(bool scrollAreaVisible);
@@ -263,14 +273,12 @@ private:
};
class AbstractBox
: public Window::LayerWidget
, public BoxContentDelegate
, protected base::Subscriber {
class AbstractBox : public Window::LayerWidget, public BoxContentDelegate {
public:
AbstractBox(
not_null<Window::LayerStackWidget*> layer,
object_ptr<BoxContent> content);
~AbstractBox();
void parentResized() override;
@@ -294,6 +302,7 @@ public:
QPointer<Ui::IconButton> addTopButton(
const style::IconButton &st,
Fn<void()> clickCallback) override;
void showLoading(bool show) override;
void updateButtonsPositions() override;
QPointer<QWidget> outerContainer() override;
@@ -332,17 +341,19 @@ protected:
}
private:
struct LoadingProgress;
void paintAdditionalTitle(Painter &p);
void updateTitlePosition();
void refreshLang();
bool hasTitle() const;
int titleHeight() const;
int buttonsHeight() const;
int buttonsTop() const;
int contentTop() const;
int countFullHeight() const;
int countRealHeight() const;
[[nodiscard]] bool hasTitle() const;
[[nodiscard]] int titleHeight() const;
[[nodiscard]] int buttonsHeight() const;
[[nodiscard]] int buttonsTop() const;
[[nodiscard]] int contentTop() const;
[[nodiscard]] int countFullHeight() const;
[[nodiscard]] int countRealHeight() const;
[[nodiscard]] QRect loadingRect() const;
void updateSize();
not_null<Window::LayerStackWidget*> _layer;
@@ -363,6 +374,7 @@ private:
std::vector<object_ptr<Ui::RoundButton>> _buttons;
object_ptr<Ui::RoundButton> _leftButton = { nullptr };
base::unique_qptr<Ui::IconButton> _topButton = { nullptr };
std::unique_ptr<LoadingProgress> _loadingProgress;
};
@@ -417,3 +429,30 @@ private:
QPointer<BoxContent> _value;
};
// Legacy global method.
namespace Ui {
namespace internal {
void showBox(
object_ptr<BoxContent> content,
LayerOptions options,
anim::type animated);
} // namespace internal
template <typename BoxType>
QPointer<BoxType> show(
object_ptr<BoxType> content,
LayerOptions options = LayerOption::CloseOther,
anim::type animated = anim::type::normal) {
auto result = QPointer<BoxType>(content.data());
internal::showBox(std::move(content), options, animated);
return result;
}
void hideLayer(anim::type animated = anim::type::normal);
void hideSettingsAndLayer(anim::type animated = anim::type::normal);
bool isLayerShown();
} // namespace Ui

View File

@@ -25,12 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "ui/special_buttons.h"
#include "ui/special_fields.h"
#include "ui/text_options.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
@@ -40,10 +41,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "observer_peer.h"
#include "main/main_session.h"
#include "facades.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace {
constexpr auto kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
constexpr auto kMaxGroupChannelTitle = 128; // See also edit_peer_info_box.
constexpr auto kMaxUserFirstLastName = 64; // See also edit_contact_box.
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
constexpr auto kMinUsernameLength = 5;
@@ -58,6 +64,48 @@ bool IsValidPhone(QString phone) {
|| phone == qsl("4242")));
}
void ChatCreateDone(
not_null<Window::SessionNavigation*> navigation,
QImage image,
const MTPUpdates &updates) {
navigation->session().api().applyUpdates(updates);
auto success = base::make_optional(&updates)
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats().v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats().v;
}
LOG(("API Error: unexpected update cons %1 "
"(GroupInfoBox::creationDone)").arg(updates->type()));
return std::nullopt;
}
| [](auto chats) {
return (!chats->empty()
&& chats->front().type() == mtpc_chat)
? base::make_optional(chats)
: std::nullopt;
}
| [&](auto chats) {
return navigation->session().data().chat(
chats->front().c_chat().vid().v);
}
| [&](not_null<ChatData*> chat) {
if (!image.isNull()) {
chat->session().api().uploadPeerPhoto(
chat,
std::move(image));
}
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
};
if (!success) {
LOG(("API Error: chat not found in updates "
"(ContactsBox::creationDone)"));
}
}
} // namespace
style::InputField CreateBioFieldStyle() {
@@ -525,48 +573,14 @@ void GroupInfoBox::createGroup(
MTP_string(title)
)).done([=](const MTPUpdates &result) {
auto image = _photo->takeResultImage();
Ui::hideLayer();
const auto navigation = _navigation;
_navigation->session().api().applyUpdates(result);
auto success = base::make_optional(&result)
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
return &updates->c_updates().vchats().v;
case mtpc_updatesCombined:
return &updates->c_updatesCombined().vchats().v;
}
LOG(("API Error: unexpected update cons %1 "
"(GroupInfoBox::creationDone)").arg(updates->type()));
return std::nullopt;
}
| [](auto chats) {
return (!chats->empty()
&& chats->front().type() == mtpc_chat)
? base::make_optional(chats)
: std::nullopt;
}
| [&](auto chats) {
return _navigation->session().data().chat(
chats->front().c_chat().vid().v);
}
| [&](not_null<ChatData*> chat) {
if (!image.isNull()) {
chat->session().api().uploadPeerPhoto(
chat,
std::move(image));
}
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
};
if (!success) {
LOG(("API Error: chat not found in updates "
"(ContactsBox::creationDone)"));
}
Ui::hideLayer(); // Destroys 'this'.
ChatCreateDone(navigation, std::move(image), result);
}).fail([=](const RPCError &error) {
_creationRequestId = 0;
if (error.type() == qstr("NO_CHAT_TITLE")) {
auto weak = make_weak(this);
auto weak = Ui::MakeWeak(this);
selectUsersBox->closeBox();
if (weak) {
_title->showError();
@@ -605,7 +619,7 @@ void GroupInfoBox::submit() {
if (_type != Type::Group) {
createChannel(title, description);
} else {
auto initBox = [title, weak = make_weak(this)](
auto initBox = [title, weak = Ui::MakeWeak(this)](
not_null<PeerListBox*> box) {
auto create = [box, title, weak] {
if (weak) {
@@ -1165,8 +1179,8 @@ void EditNameBox::prepare() {
if (_invertOrder) {
setTabOrder(_last, _first);
}
_first->setMaxLength(kMaxGroupChannelTitle);
_last->setMaxLength(kMaxGroupChannelTitle);
_first->setMaxLength(kMaxUserFirstLastName);
_last->setMaxLength(kMaxUserFirstLastName);
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
connect(_last, &Ui::InputField::submitted, [=] { submit(); });
@@ -1349,7 +1363,7 @@ void RevokePublicLinkBox::Inner::updateSelected() {
PeerData *selected = nullptr;
auto top = _rowsTop;
for (const auto &row : _rows) {
auto revokeLink = rtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - _revokeWidth, top + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _revokeWidth, st::normalFont->height, width());
auto revokeLink = style::rtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - _revokeWidth, top + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _revokeWidth, st::normalFont->height, width());
if (revokeLink.contains(point)) {
selected = row.peer;
break;

View File

@@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "styles/style_widgets.h"
#include <QtCore/QTimer>
class ConfirmBox;
class PeerListBox;
@@ -137,7 +139,10 @@ private:
};
class SetupChannelBox : public BoxContent, public RPCSender {
class SetupChannelBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
SetupChannelBox(
QWidget*,
@@ -232,7 +237,10 @@ private:
};
class RevokePublicLinkBox : public BoxContent, public RPCSender {
class RevokePublicLinkBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
RevokePublicLinkBox(
QWidget*,

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "mainwindow.h"
#include "ui/widgets/checkbox.h"
#include "facades.h"
#include "styles/style_boxes.h"
AutoLockBox::AutoLockBox(QWidget*, not_null<Main::Session*> session)

View File

@@ -10,12 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/effects/round_checkbox.h"
#include "ui/image/image.h"
#include "ui/ui_utility.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "mtproto/sender.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "boxes/background_preview_box.h"
#include "boxes/confirm_box.h"
#include "app.h"
#include "styles/style_overview.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
@@ -153,7 +156,7 @@ void BackgroundBox::prepare() {
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto session = _session;
const auto remove = [=, weak = make_weak(this)]{
const auto remove = [=, weak = Ui::MakeWeak(this)]{
if (*box) {
(*box)->closeBox();
}
@@ -329,7 +332,7 @@ void BackgroundBox::Inner::paintPaper(
if (paper.data.id() == Window::Theme::Background()->id()) {
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
_check->paint(p, crl::now(), checkLeft, checkTop, width());
_check->paint(p, checkLeft, checkTop, width());
} else if (Data::IsCloudWallPaper(paper.data)
&& !Data::IsDefaultWallPaper(paper.data)
&& over.has_value()

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/image/image.h"
#include "ui/widgets/checkbox.h"
#include "ui/ui_utility.h"
#include "history/history.h"
#include "history/history_message.h"
#include "history/view/history_view_message.h"
@@ -24,9 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "boxes/confirm_box.h"
#include "boxes/background_preview_box.h"
#include "app.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include <QtGui/QClipboard>
#include <QtGui/QGuiApplication>
namespace {
constexpr auto kMaxWallPaperSlugLength = 255;
@@ -221,7 +226,7 @@ void ServiceCheck::Generator::invalidate() {
ServiceCheck::Generator &ServiceCheck::Frames() {
static const auto Instance = Ui::CreateChild<Generator>(
QApplication::instance());
QCoreApplication::instance());
return *Instance;
}
@@ -284,12 +289,14 @@ AdminLog::OwnedItem GenerateTextItem(
const auto flags = Flag::f_entities
| Flag::f_from_id
| (out ? Flag::f_out : Flag(0));
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
const auto replyTo = 0;
const auto viaBotId = 0;
const auto item = history->owner().makeMessage(
history,
++id,
flags,
clientFlags,
replyTo,
viaBotId,
base::unixtime::now(),
@@ -496,7 +503,7 @@ void BackgroundPreviewBox::apply() {
}
void BackgroundPreviewBox::share() {
QApplication::clipboard()->setText(_paper.shareUrl());
QGuiApplication::clipboard()->setText(_paper.shareUrl());
Ui::Toast::Show(tr::lng_background_link_copied(tr::now));
}

View File

@@ -25,7 +25,8 @@ class Checkbox;
class BackgroundPreviewBox
: public BoxContent
, private HistoryView::SimpleElementDelegate {
, private HistoryView::SimpleElementDelegate
, private base::Subscriber {
public:
BackgroundPreviewBox(
QWidget*,

View File

@@ -161,6 +161,12 @@ boxPhotoCompressedSkip: 20px;
boxPhotoCaptionSkip: 8px;
boxPhotoTextFg: windowSubTextFg;
boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
color: windowSubTextFg;
thickness: 2px;
}
boxLoadingSize: 20px;
cropPointSize: 10px;
cropSkip: 13px;
cropMinSize: 20px;
@@ -321,12 +327,14 @@ contactsMultiSelect: MultiSelect {
}
contactsPhotoCheckIcon: icon {{
"default_checkbox_check",
windowFgActive,
overviewCheckFgActive,
point(3px, 6px)
}};
contactsPhotoCheck: RoundCheckbox(defaultRoundCheckbox) {
size: 20px;
sizeSmall: 0.3;
bgInactive: overviewCheckBg;
bgActive: overviewCheckBgActive;
check: contactsPhotoCheckIcon;
}
contactsPhotoCheckbox: RoundImageCheckbox {
@@ -877,6 +885,21 @@ themesScroll: ScrollArea(defaultScrollArea) {
bottomsh: 0px;
topsh: 0px;
}
themesMenuToggle: IconButton(defaultIconButton) {
width: 44px;
height: 44px;
icon: icon {{ "title_menu_dots", menuIconFg }};
iconOver: icon {{ "title_menu_dots", menuIconFgOver }};
iconPosition: point(-1px, -1px);
rippleAreaPosition: point(4px, 4px);
rippleAreaSize: 36px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
themesMenuPosition: point(-2px, 25px);
createPollField: InputField(defaultInputField) {
font: boxTextFont;

View File

@@ -8,10 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/calendar_box.h"
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "lang/lang_keys.h"
#include "ui/effects/ripple_animation.h"
#include "ui/ui_utility.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
namespace {
@@ -27,6 +28,11 @@ public:
_month.setForced(_month.value(), true);
}
void setBeginningButton(bool enabled);
bool hasBeginningButton() const {
return _beginningButton;
}
void setMinDate(QDate date);
void setMaxDate(QDate date);
@@ -55,6 +61,9 @@ public:
bool isEnabled(int index) const {
return (index >= _minDayIndex) && (index <= _maxDayIndex);
}
bool atBeginning() const {
return _highlighted == _min;
}
const base::Variable<QDate> &month() {
return _month;
@@ -69,6 +78,8 @@ private:
static int daysShiftForMonth(QDate month);
static int rowsCountForMonth(QDate month);
bool _beginningButton = false;
base::Variable<QDate> _month;
QDate _min, _max;
QDate _highlighted;
@@ -86,6 +97,10 @@ CalendarBox::Context::Context(QDate month, QDate highlighted) : _highlighted(hig
showMonth(month);
}
void CalendarBox::Context::setBeginningButton(bool enabled) {
_beginningButton = enabled;
}
void CalendarBox::Context::setMinDate(QDate date) {
_min = date;
applyMonth(_month.value(), true);
@@ -192,6 +207,7 @@ public:
int countHeight();
void setDateChosenCallback(Fn<void(QDate)> callback);
void selectBeginning();
~Inner();
@@ -243,7 +259,7 @@ void CalendarBox::Inner::monthChanged(QDate month) {
_ripples.clear();
resizeToCurrent();
update();
sendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
Ui::SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
}
void CalendarBox::Inner::resizeToCurrent() {
@@ -389,7 +405,10 @@ void CalendarBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
auto pressed = _pressed;
setPressed(kEmptySelection);
if (pressed != kEmptySelection && pressed == _selected) {
_dateChosenCallback(_context->dateFromIndex(pressed));
crl::on_main(this, [=] {
const auto onstack = _dateChosenCallback;
onstack(_context->dateFromIndex(pressed));
});
}
}
@@ -417,6 +436,10 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
_dateChosenCallback = std::move(callback);
}
void CalendarBox::Inner::selectBeginning() {
_dateChosenCallback(_context->dateFromIndex(_context->minDayIndex()));
}
CalendarBox::Inner::~Inner() = default;
class CalendarBox::Title : public TWidget, private base::Subscriber {
@@ -494,6 +517,14 @@ void CalendarBox::setMaxDate(QDate date) {
_context->setMaxDate(date);
}
bool CalendarBox::hasBeginningButton() const {
return _context->hasBeginningButton();
}
void CalendarBox::setBeginningButton(bool enabled) {
_context->setBeginningButton(enabled);
}
void CalendarBox::prepare() {
_previous->setClickedCallback([this] { goPreviousMonth(); });
_next->setClickedCallback([this] { goNextMonth(); });
@@ -510,6 +541,9 @@ void CalendarBox::prepare() {
if (_finalize) {
_finalize(this);
}
if (!_context->atBeginning() && hasBeginningButton()) {
addLeftButton(tr::lng_calendar_beginning(), [this] { _inner->selectBeginning(); });
}
}
bool CalendarBox::isPreviousEnabled() const {
@@ -555,6 +589,8 @@ void CalendarBox::resizeEvent(QResizeEvent *e) {
void CalendarBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
e->ignore();
} else if (e->key() == Qt::Key_Home) {
_inner->selectBeginning();
} else if (e->key() == Qt::Key_Left) {
goPreviousMonth();
} else if (e->key() == Qt::Key_Right) {

View File

@@ -17,7 +17,7 @@ namespace Ui {
class IconButton;
} // namespace Ui
class CalendarBox : public BoxContent {
class CalendarBox : public BoxContent, private base::Subscriber {
public:
CalendarBox(
QWidget*,
@@ -33,6 +33,8 @@ public:
FnMut<void(not_null<CalendarBox*>)> finalize,
const style::CalendarSizes &st);
void setBeginningButton(bool enabled);
bool hasBeginningButton() const;
void setMinDate(QDate date);
void setMaxDate(QDate date);

View File

@@ -13,11 +13,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/fade_wrap.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "ui/special_fields.h"
#include "boxes/confirm_phone_box.h"
#include "boxes/confirm_box.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "app.h"
#include "styles/style_boxes.h"
namespace {
@@ -308,7 +310,7 @@ void ChangePhoneBox::EnterCode::submit() {
const auto session = _session;
const auto code = _code->getDigitsOnly();
const auto weak = make_weak(this);
const auto weak = Ui::MakeWeak(this);
_requestId = MTP::send(MTPaccount_ChangePhone(
MTP_string(_phone),
MTP_string(_hash),

View File

@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h"
#include "window/window_session_controller.h"
#include "storage/localstorage.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_photo.h"
#include "data/data_channel.h"
@@ -33,6 +34,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "main/main_session.h"
#include "observer_peer.h"
#include "facades.h"
#include "app.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseMarkdown | TextParseRichText, // flags
@@ -248,8 +254,9 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateHover();
if (const auto activated = ClickHandler::unpressed()) {
const auto guard = window();
Ui::hideLayer();
App::activateClickHandler(activated, e->button());
ActivateClickHandler(guard, activated, e->button());
return;
}
BoxContent::mouseReleaseEvent(e);
@@ -542,10 +549,11 @@ void DeleteMessagesBox::prepare() {
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
if (const auto peer = checkFromSinglePeer()) {
auto count = int(_ids.size());
if (auto revoke = revokeText(peer)) {
if (hasScheduledMessages()) {
} else if (auto revoke = revokeText(peer)) {
_revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox);
appendDetails(std::move(revoke->description));
} else if (peer && peer->isChannel()) {
} else if (peer->isChannel()) {
if (peer->isMegagroup()) {
appendDetails({ tr::lng_delete_for_everyone_hint(tr::now, lt_count, count) });
}
@@ -580,6 +588,17 @@ void DeleteMessagesBox::prepare() {
setDimensions(st::boxWidth, fullHeight);
}
bool DeleteMessagesBox::hasScheduledMessages() const {
for (const auto fullId : std::as_const(_ids)) {
if (const auto item = _session->data().message(fullId)) {
if (item->isScheduled()) {
return true;
}
}
}
return false;
}
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
auto result = (PeerData*)nullptr;
for (const auto fullId : std::as_const(_ids)) {
@@ -765,9 +784,21 @@ void DeleteMessagesBox::deleteAndClear() {
}
base::flat_map<not_null<PeerData*>, QVector<MTPint>> idsByPeer;
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
for (const auto itemId : _ids) {
if (const auto item = _session->data().message(itemId)) {
const auto history = item->history();
if (item->isScheduled()) {
const auto wasOnServer = !item->isSending()
&& !item->hasFailed();
if (wasOnServer) {
scheduledIdsByPeer[history->peer].push_back(MTP_int(
_session->data().scheduledMessages().lookupId(item)));
} else {
_session->data().scheduledMessages().removeSending(item);
}
continue;
}
const auto wasOnServer = IsServerMsgId(item->id);
const auto wasLast = (history->lastMessage() == item);
const auto wasInChats = (history->chatListMessage() == item);
@@ -784,6 +815,15 @@ void DeleteMessagesBox::deleteAndClear() {
for (const auto &[peer, ids] : idsByPeer) {
peer->session().api().deleteMessages(peer, ids, revoke);
}
for (const auto &[peer, ids] : scheduledIdsByPeer) {
peer->session().api().request(MTPmessages_DeleteScheduledMessages(
peer->input,
MTP_vector<MTPint>(ids)
)).done([=, peer=peer](const MTPUpdates &updates) {
peer->session().api().applyUpdates(updates);
}).send();
}
const auto session = _session;
Ui::hideLayer();
session->data().sendHistoryChangeNotifications();
@@ -870,7 +910,9 @@ void ConfirmInviteBox::prepare() {
for (const auto user : _participants) {
auto name = new Ui::FlatLabel(this, st::confirmInviteUserName);
name->resizeToWidth(st::confirmInviteUserPhotoSize + padding);
name->setText(user->firstName.isEmpty() ? App::peerName(user) : user->firstName);
name->setText(user->firstName.isEmpty()
? user->name
: user->firstName);
name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop);
left += _userWidth;
}

View File

@@ -95,7 +95,7 @@ public:
};
class MaxInviteBox : public BoxContent {
class MaxInviteBox : public BoxContent, private base::Subscriber {
public:
MaxInviteBox(QWidget*, not_null<ChannelData*> channel);
@@ -176,8 +176,10 @@ private:
TextWithEntities description;
};
void deleteAndClear();
PeerData *checkFromSinglePeer() const;
std::optional<RevokeConfig> revokeText(not_null<PeerData*> peer) const;
[[nodiscard]] PeerData *checkFromSinglePeer() const;
[[nodiscard]] bool hasScheduledMessages() const;
[[nodiscard]] std::optional<RevokeConfig> revokeText(
not_null<PeerData*> peer) const;
const not_null<Main::Session*> _session;
@@ -199,7 +201,10 @@ private:
};
class ConfirmInviteBox : public BoxContent, public RPCSender {
class ConfirmInviteBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
ConfirmInviteBox(
QWidget*,

View File

@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_info.h" // Platform::SystemVersionPretty
#include "mainwidget.h"
#include "numbers.h"
#include "app.h"
#include "lang/lang_keys.h"
namespace {

View File

@@ -24,10 +24,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/text_options.h"
#include "facades.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace {
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
@@ -411,7 +415,7 @@ void ProxyRow::paintCheck(Painter &p) {
pen.setCapStyle(Qt::RoundCap);
p.setPen(pen);
p.setBrush(_st->bg);
const auto rect = rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth);
const auto rect = style::rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth);
if (_progress && loading.shown > 0 && anim::Disabled()) {
anim::DrawStaticLoading(
p,
@@ -430,7 +434,7 @@ void ProxyRow::paintCheck(Painter &p) {
p.setBrush(anim::brush(_st->untoggledFg, _st->toggledFg, toggled * set));
auto skip0 = _st->diameter / 2., skip1 = _st->skip / 10., checkSkip = skip0 * (1. - toggled) + skip1 * toggled;
p.drawEllipse(rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip)), outerWidth));
p.drawEllipse(style::rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip)), outerWidth));
}
}
@@ -1426,7 +1430,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) {
? "&pass=" + qthelp::url_encode(proxy.password) : "")
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
? "&secret=" + proxy.password : "");
QApplication::clipboard()->setText(link);
QGuiApplication::clipboard()->setText(link);
Ui::Toast::Show(tr::lng_username_copied(tr::now));
}
@@ -1434,7 +1438,7 @@ ProxiesBoxController::~ProxiesBoxController() {
if (_saveTimer.isActive()) {
App::CallDelayed(
kSaveSettingsDelayedTimeout,
QApplication::instance(),
QCoreApplication::instance(),
[] { Local::writeSettings(); });
}
}

View File

@@ -19,8 +19,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "core/event_filter.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "history/view/history_view_schedule_box.h"
#include "settings/settings_common.h"
#include "base/unique_qptr.h"
#include "facades.h"
#include "styles/style_boxes.h"
#include "styles/style_settings.h"
@@ -521,11 +524,11 @@ void Options::addEmptyOption() {
Core::InstallEventFilter(field, [=](not_null<QEvent*> event) {
if (event->type() != QEvent::KeyPress
|| !field->getLastText().isEmpty()) {
return false;
return Core::EventFilter::Result::Continue;
}
const auto key = static_cast<QKeyEvent*>(event.get())->key();
if (key != Qt::Key_Backspace) {
return false;
return Core::EventFilter::Result::Continue;
}
const auto index = findField(field);
@@ -534,7 +537,7 @@ void Options::addEmptyOption() {
} else {
_backspaceInFront.fire({});
}
return true;
return Core::EventFilter::Result::Cancel;
});
_list.back().removeClicks(
@@ -592,11 +595,15 @@ void Options::checkLastOption() {
} // namespace
CreatePollBox::CreatePollBox(QWidget*, not_null<Main::Session*> session)
: _session(session) {
CreatePollBox::CreatePollBox(
QWidget*,
not_null<Main::Session*> session,
Api::SendType sendType)
: _session(session)
, _sendType(sendType) {
}
rpl::producer<PollData> CreatePollBox::submitRequests() const {
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
return _submitRequests.events();
}
@@ -703,6 +710,22 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
result.answers = options->toPollAnswers();
return result;
};
const auto send = [=](Api::SendOptions options) {
_submitRequests.fire({ collectResult(), options });
};
const auto sendSilent = [=] {
auto options = Api::SendOptions();
options.silent = true;
send(options);
};
const auto sendScheduled = [=] {
Ui::show(
HistoryView::PrepareScheduleBox(
this,
SendMenuType::Scheduled,
send),
LayerOption::KeepOther);
};
const auto updateValid = [=] {
valid->fire(isValidQuestion() && options->isValid());
};
@@ -715,9 +738,16 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
) | rpl::start_with_next([=](bool valid) {
clearButtons();
if (valid) {
addButton(
const auto submit = addButton(
tr::lng_polls_create_button(),
[=] { _submitRequests.fire(collectResult()); });
[=] { send({}); });
if (_sendType == Api::SendType::Normal) {
SetupSendMenu(
submit.data(),
[=] { return SendMenuType::Scheduled; },
sendSilent,
sendScheduled);
}
}
addButton(tr::lng_cancel(), [=] { closeBox(); });
}, lifetime());

View File

@@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "api/api_common.h"
#include "data/data_poll.h"
struct PollData;
@@ -21,9 +23,17 @@ class Session;
class CreatePollBox : public BoxContent {
public:
CreatePollBox(QWidget*, not_null<Main::Session*> session);
struct Result {
PollData poll;
Api::SendOptions options;
};
rpl::producer<PollData> submitRequests() const;
CreatePollBox(
QWidget*,
not_null<Main::Session*> session,
Api::SendType sendType);
rpl::producer<Result> submitRequests() const;
void submitFailed(const QString &error);
void setInnerFocus() override;
@@ -37,8 +47,9 @@ private:
not_null<Ui::VerticalLayout*> container);
const not_null<Main::Session*> _session;
const Api::SendType _sendType = Api::SendType();
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;
rpl::event_stream<PollData> _submitRequests;
rpl::event_stream<Result> _submitRequests;
};

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "platform/platform_specific.h"
#include "facades.h"
#include "styles/style_boxes.h"
DownloadPathBox::DownloadPathBox(QWidget *parent)

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_caption_box.h"
#include "apiwrap.h"
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
@@ -21,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
@@ -31,12 +33,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_history.h"
#include "ui/image/image.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/checkbox.h"
#include "ui/special_buttons.h"
#include "ui/text_options.h"
#include "ui/widgets/input_fields.h"
#include "window/window_session_controller.h"
#include "ui/widgets/checkbox.h"
#include "confirm_box.h"
#include "facades.h"
#include "app.h"
#include <QtCore/QMimeData>
EditCaptionBox::EditCaptionBox(
QWidget*,
@@ -277,14 +284,13 @@ EditCaptionBox::EditCaptionBox(
}, _wayWrap->lifetime());
}
bool EditCaptionBox::emojiFilter(not_null<QEvent*> event) {
void EditCaptionBox::emojiFilterForGeometry(not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::Move || type == QEvent::Resize) {
// updateEmojiPanelGeometry uses not only container geometry, but
// also container children geometries that will be updated later.
crl::on_main(this, [=] { updateEmojiPanelGeometry(); });
}
return false;
}
void EditCaptionBox::updateEmojiPanelGeometry() {
@@ -711,14 +717,16 @@ void EditCaptionBox::setupEmojiPanel() {
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
_emojiPanel->hide();
_emojiPanel->getSelector()->emojiChosen(
_emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](EmojiPtr emoji) {
Ui::InsertEmojiAtCursor(_field->textCursor(), emoji);
}, lifetime());
_emojiFilter.reset(Core::InstallEventFilter(
container,
[=](not_null<QEvent*> event) { return emojiFilter(event); }));
const auto filterCallback = [=](not_null<QEvent*> event) {
emojiFilterForGeometry(event);
return Core::EventFilter::Result::Continue;
};
_emojiFilter.reset(Core::InstallEventFilter(container, filterCallback));
_emojiToggle.create(this, st::boxAttachEmoji);
_emojiToggle->installEventFilter(_emojiPanel);
@@ -807,10 +815,10 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
// App::roundRect(p, x, y, w, h, st::msgInBg, MessageInCorners, &st::msgInShadow);
if (_thumbw) {
QRect rthumb(rtlrect(x + 0, y + 0, st::msgFileThumbSize, st::msgFileThumbSize, width()));
QRect rthumb(style::rtlrect(x + 0, y + 0, st::msgFileThumbSize, st::msgFileThumbSize, width()));
p.drawPixmap(rthumb.topLeft(), _thumb);
} else {
const QRect inner(rtlrect(x + 0, y + 0, st::msgFileSize, st::msgFileSize, width()));
const QRect inner(style::rtlrect(x + 0, y + 0, st::msgFileSize, st::msgFileSize, width()));
p.setPen(Qt::NoPen);
p.setBrush(st::msgFileInBg);
@@ -893,7 +901,7 @@ void EditCaptionBox::save() {
const auto textWithTags = _field->getTextWithAppliedMarkdown();
auto sending = TextWithEntities{
textWithTags.text,
ConvertTextTagsToEntities(textWithTags.tags)
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
};
const auto prepareFlags = Ui::ItemTextOptions(
item->history(),
@@ -901,9 +909,9 @@ void EditCaptionBox::save() {
TextUtilities::PrepareForSending(sending, prepareFlags);
TextUtilities::Trim(sending);
const auto sentEntities = TextUtilities::EntitiesToMTP(
const auto sentEntities = Api::EntitiesToMTP(
sending.entities,
TextUtilities::ConvertOption::SkipLocal);
Api::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
flags |= MTPmessages_EditMessage::Flag::f_entities;
}
@@ -912,7 +920,7 @@ void EditCaptionBox::save() {
const auto textWithTags = _field->getTextWithAppliedMarkdown();
auto sending = TextWithEntities{
textWithTags.text,
ConvertTextTagsToEntities(textWithTags.tags)
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
};
item->setText(sending);
@@ -920,7 +928,7 @@ void EditCaptionBox::save() {
std::move(_preparedList),
(!_asFile && _photo) ? SendMediaType::Photo : SendMediaType::File,
_field->getTextWithAppliedMarkdown(),
ApiWrap::SendOptions(item->history()),
Api::SendAction(item->history()),
item->fullId().msg);
closeBox();
return;
@@ -934,7 +942,8 @@ void EditCaptionBox::save() {
MTP_string(sending.text),
MTPInputMedia(),
MTPReplyMarkup(),
sentEntities),
sentEntities,
MTP_int(0)), // schedule_date
rpcDone(&EditCaptionBox::saveDone),
rpcFail(&EditCaptionBox::saveFail));
}

View File

@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "storage/storage_media_prepare.h"
#include "ui/wrap/slide_wrap.h"
#include <rpl/event_stream.h>
#include "media/clip/media_clip_reader.h"
namespace ChatHelpers {
class TabbedPanel;
@@ -35,7 +35,10 @@ namespace Window {
class SessionController;
} // namespace Window
class EditCaptionBox : public BoxContent, public RPCSender {
class EditCaptionBox
: public BoxContent
, public RPCSender
, private base::Subscriber {
public:
EditCaptionBox(
QWidget*,
@@ -57,7 +60,7 @@ private:
void setupEmojiPanel();
void updateEmojiPanelGeometry();
bool emojiFilter(not_null<QEvent*> event);
void emojiFilterForGeometry(not_null<QEvent*> event);
void save();
void captionResized();

View File

@@ -8,15 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/edit_color_box.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "ui/widgets/shadow.h"
#include "platform/platform_info.h"
#include "styles/style_mediaview.h"
#include "ui/widgets/input_fields.h"
#include "ui/ui_utility.h"
#include "platform/platform_info.h"
#include "app.h"
#include "styles/style_boxes.h"
#include "styles/style_mediaview.h"
class EditColorBox::Picker : public TWidget {
public:
Picker(QWidget *parent, QColor color);
Picker(QWidget *parent, Mode mode, QColor color);
float64 valueX() const {
return _x;
@@ -28,7 +30,7 @@ public:
base::Observable<void> &changed() {
return _changed;
}
void setHSV(int hue, int saturation, int brightness);
void setHSB(HSB hsb);
void setRGB(int red, int green, int blue);
protected:
@@ -43,8 +45,11 @@ private:
QCursor generateCursor();
void preparePalette();
void preparePaletteRGBA();
void preparePaletteHSL();
void updateCurrentPoint(QPoint localPosition);
Mode _mode;
QColor _topleft;
QColor _topright;
QColor _bottomleft;
@@ -61,8 +66,8 @@ private:
};
QCursor EditColorBox::Picker::generateCursor() {
auto diameter = ConvertScale(16);
auto line = ConvertScale(1);
auto diameter = style::ConvertScale(16);
auto line = style::ConvertScale(1);
auto size = ((diameter + 2 * line) >= 32) ? 64 : 32;
auto cursor = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cursor.setDevicePixelRatio(cRetinaFactor());
@@ -84,7 +89,9 @@ QCursor EditColorBox::Picker::generateCursor() {
return QCursor(QPixmap::fromImage(cursor));
}
EditColorBox::Picker::Picker(QWidget *parent, QColor color) : TWidget(parent) {
EditColorBox::Picker::Picker(QWidget *parent, Mode mode, QColor color)
: TWidget(parent)
, _mode(mode) {
setCursor(generateCursor());
auto size = QSize(st::colorPickerSize, st::colorPickerSize);
@@ -137,18 +144,27 @@ void EditColorBox::Picker::preparePalette() {
if (!_paletteInvalidated) return;
_paletteInvalidated = false;
auto size = _palette.width();
if (_mode == Mode::RGBA) {
preparePaletteRGBA();
} else {
preparePaletteHSL();
}
_palette.setDevicePixelRatio(cRetinaFactor());
}
void EditColorBox::Picker::preparePaletteRGBA() {
const auto size = _palette.width();
auto ints = reinterpret_cast<uint32*>(_palette.bits());
auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
auto part = Large / size;
const auto part = Large / size;
auto topleft = anim::shifted(_topleft);
auto topright = anim::shifted(_topright);
auto bottomleft = anim::shifted(_bottomleft);
auto bottomright = anim::shifted(_bottomright);
const auto topleft = anim::shifted(_topleft);
const auto topright = anim::shifted(_topright);
const auto bottomleft = anim::shifted(_bottomleft);
const auto bottomright = anim::shifted(_bottomright);
auto y_accumulated = 0;
for (auto y = 0; y != size; ++y, y_accumulated += part) {
@@ -156,11 +172,11 @@ void EditColorBox::Picker::preparePalette() {
// 0 <= y_accumulated < Large
// 0 <= y_ratio < 256
auto top_ratio = 255 - y_ratio;
auto bottom_ratio = y_ratio;
const auto top_ratio = 256 - y_ratio;
const auto bottom_ratio = y_ratio;
auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
const auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
const auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
@@ -168,7 +184,7 @@ void EditColorBox::Picker::preparePalette() {
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
auto left_ratio = 255 - x_ratio;
auto left_ratio = 256 - x_ratio;
auto right_ratio = x_ratio;
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
@@ -177,6 +193,40 @@ void EditColorBox::Picker::preparePalette() {
}
}
void EditColorBox::Picker::preparePaletteHSL() {
const auto size = _palette.width();
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
auto ints = reinterpret_cast<uint32*>(_palette.bits());
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
const auto part = Large / size;
const auto lightness = _topleft.lightness();
const auto right = anim::shifted(_bottomright);
for (auto y = 0; y != size; ++y) {
const auto hue = y * 360 / size;
const auto color = QColor::fromHsl(hue, 255, lightness).toRgb();
const auto left = anim::shifted(anim::getPremultiplied(color));
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
auto x_ratio = x_accumulated >> (LargeBit - 8); // (x_accumulated * 256) / Large;
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
auto left_ratio = 256 - x_ratio;
auto right_ratio = x_ratio;
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
}
ints += intsAddPerLine;
}
_palette = std::move(_palette).transformed(
QTransform(0, 1, 1, 0, 0, 0));
}
void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
auto x = snap(localPosition.x(), 0, width()) / float64(width());
auto y = snap(localPosition.y(), 0, height()) / float64(height());
@@ -188,17 +238,25 @@ void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
}
}
void EditColorBox::Picker::setHSV(int hue, int saturation, int brightness) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
void EditColorBox::Picker::setHSB(HSB hsb) {
if (_mode == Mode::RGBA) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hsb.hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
_x = snap(hsb.saturation / 255., 0., 1.);
_y = 1. - snap(hsb.brightness / 255., 0., 1.);
} else {
_topleft = _topright = QColor::fromHsl(0, 255, hsb.brightness);
_bottomleft = _bottomright = QColor::fromHsl(0, 0, hsb.brightness);
_x = snap(hsb.hue / 360., 0., 1.);
_y = 1. - snap(hsb.saturation / 255., 0., 1.);
}
_paletteInvalidated = true;
update();
_x = snap(saturation / 255., 0., 1.);
_y = 1. - snap(brightness / 255., 0., 1.);
}
void EditColorBox::Picker::setRGB(int red, int green, int blue) {
@@ -206,7 +264,11 @@ void EditColorBox::Picker::setRGB(int red, int green, int blue) {
}
void EditColorBox::Picker::setFromColor(QColor color) {
setHSV(color.hsvHue(), color.hsvSaturation(), color.value());
if (_mode == Mode::RGBA) {
setHSB({ color.hsvHue(), color.hsvSaturation(), color.value() });
} else {
setHSB({ color.hslHue(), color.hslSaturation(), color.lightness() });
}
}
class EditColorBox::Slider : public TWidget {
@@ -218,6 +280,7 @@ public:
enum class Type {
Hue,
Opacity,
Lightness
};
Slider(QWidget *parent, Direction direction, Type type, QColor color);
@@ -231,10 +294,12 @@ public:
_value = snap(value, 0., 1.);
update();
}
void setHSV(int hue, int saturation, int brightness);
void setHSB(HSB hsb);
void setRGB(int red, int green, int blue);
void setAlpha(int alpha);
void setLightnessLimits(int min, int max);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
@@ -254,10 +319,14 @@ private:
void generatePixmap();
void updatePixmapFromMask();
void updateCurrentPoint(QPoint localPosition);
[[nodiscard]] QColor applyLimits(QColor color) const;
Direction _direction = Direction::Horizontal;
Type _type = Type::Hue;
int _lightnessMin = 0;
int _lightnessMax = 255;
QColor _color;
float64 _value = 0;
@@ -270,12 +339,17 @@ private:
};
EditColorBox::Slider::Slider(QWidget *parent, Direction direction, Type type, QColor color) : TWidget(parent)
EditColorBox::Slider::Slider(
QWidget *parent,
Direction direction,
Type type,
QColor color)
: TWidget(parent)
, _direction(direction)
, _type(type)
, _color(color.red(), color.green(), color.blue())
, _value(valueFromColor(color))
, _transparent((_type == Type::Hue) ? QBrush() : style::transparentPlaceholderBrush()) {
, _transparent((_type == Type::Opacity) ? style::transparentPlaceholderBrush() : QBrush()) {
prepareMinSize();
}
@@ -336,10 +410,9 @@ void EditColorBox::Slider::generatePixmap() {
auto part = Large / size;
if (_type == Type::Hue) {
QColor color;
for (auto x = 0; x != size; ++x) {
color.setHsv(x * 360 / size, 255, 255);
auto value = anim::getPremultiplied(color.toRgb());
const auto color = QColor::fromHsv(x * 360 / size, 255, 255);
const auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
@@ -349,7 +422,7 @@ void EditColorBox::Slider::generatePixmap() {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
} else {
} else if (_type == Type::Opacity) {
auto color = anim::shifted(QColor(255, 255, 255, 255));
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
@@ -359,7 +432,7 @@ void EditColorBox::Slider::generatePixmap() {
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
*ints++ = anim::unshifted(color * x_ratio + transparent * (255 - x_ratio));
*ints++ = anim::unshifted(color * x_ratio + transparent * (256 - x_ratio));
}
ints += intsPerLineAdded;
}
@@ -368,22 +441,45 @@ void EditColorBox::Slider::generatePixmap() {
}
_mask = std::move(image);
updatePixmapFromMask();
} else {
const auto range = _lightnessMax - _lightnessMin;
for (auto x = 0; x != size; ++x) {
const auto color = QColor::fromHsl(
_color.hslHue(),
_color.hslSaturation(),
_lightnessMin + x * range / size);
const auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
++ints;
}
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
}
}
void EditColorBox::Slider::setHSV(int hue, int saturation, int brightness) {
void EditColorBox::Slider::setHSB(HSB hsb) {
if (_type == Type::Hue) {
// hue == 360 converts to 0 if done in general way
_value = valueFromHue(hue);
_value = valueFromHue(hsb.hue);
update();
} else if (_type == Type::Opacity) {
_color.setHsv(hsb.hue, hsb.saturation, hsb.brightness);
colorUpdated();
} else {
_color.setHsv(hue, saturation, brightness);
_color.setHsl(
hsb.hue,
hsb.saturation,
std::clamp(hsb.brightness, _lightnessMin, _lightnessMax));
colorUpdated();
}
}
void EditColorBox::Slider::setRGB(int red, int green, int blue) {
_color.setRgb(red, green, blue);
_color = applyLimits(QColor(red, green, blue));
colorUpdated();
}
@@ -392,12 +488,23 @@ void EditColorBox::Slider::colorUpdated() {
_value = valueFromColor(_color);
} else if (!_mask.isNull()) {
updatePixmapFromMask();
} else {
_value = valueFromColor(_color);
generatePixmap();
}
update();
}
float64 EditColorBox::Slider::valueFromColor(QColor color) const {
return (_type == Type::Hue) ? valueFromHue(color.hsvHue()) : color.alphaF();
return (_type == Type::Hue)
? valueFromHue(color.hsvHue())
: (_type == Type::Opacity)
? color.alphaF()
: std::clamp(
((color.lightness() - _lightnessMin)
/ float64(_lightnessMax - _lightnessMin)),
0.,
1.);
}
float64 EditColorBox::Slider::valueFromHue(int hue) const {
@@ -411,6 +518,15 @@ void EditColorBox::Slider::setAlpha(int alpha) {
}
}
void EditColorBox::Slider::setLightnessLimits(int min, int max) {
Expects(max > min);
_lightnessMin = min;
_lightnessMax = max;
_color = applyLimits(_color);
colorUpdated();
}
void EditColorBox::Slider::updatePixmapFromMask() {
_pixmap = App::pixmapFromImageInPlace(style::colorizeImage(_mask, _color));
}
@@ -426,6 +542,19 @@ void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) {
}
}
QColor EditColorBox::Slider::applyLimits(QColor color) const {
if (_type != Type::Lightness) {
return color;
}
const auto lightness = color.lightness();
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
if (clamped == lightness) {
return color;
}
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
}
class EditColorBox::Field : public Ui::MaskedInputField {
public:
Field(QWidget *parent, const style::InputField &st, const QString &placeholder, int limit, const QString &units = QString());
@@ -613,14 +742,18 @@ void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
}
EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : BoxContent()
EditColorBox::EditColorBox(
QWidget*,
const QString &title,
Mode mode,
QColor current)
: BoxContent()
, _title(title)
, _picker(this, current)
, _hueSlider(this, Slider::Direction::Vertical, Slider::Type::Hue, current)
, _opacitySlider(this, Slider::Direction::Horizontal, Slider::Type::Opacity, current)
, _mode(mode)
, _picker(this, mode, current)
, _hueField(this, st::colorValueInput, "H", 360, QString() + QChar(176)) // degree character
, _saturationField(this, st::colorValueInput, "S", 100, "%")
, _brightnessField(this, st::colorValueInput, "B", 100, "%")
, _brightnessField(this, st::colorValueInput, (mode == Mode::RGBA) ? "B" : "L", 100, "%")
, _redField(this, st::colorValueInput, "R", 255)
, _greenField(this, st::colorValueInput, "G", 255)
, _blueField(this, st::colorValueInput, "B", 255)
@@ -628,16 +761,47 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
, _transparent(style::transparentPlaceholderBrush())
, _current(current)
, _new(current) {
if (_mode == Mode::RGBA) {
_hueSlider.create(
this,
Slider::Direction::Vertical,
Slider::Type::Hue,
current);
_opacitySlider.create(
this,
Slider::Direction::Horizontal,
Slider::Type::Opacity,
current);
} else if (_mode == Mode::HSL) {
_lightnessSlider.create(
this,
Slider::Direction::Horizontal,
Slider::Type::Lightness,
current);
}
}
void EditColorBox::setLightnessLimits(int min, int max) {
Expects(_mode == Mode::HSL);
_lightnessMin = min;
_lightnessMax = max;
_lightnessSlider->setLightnessLimits(min, max);
const auto adjusted = applyLimits(_new);
if (_new != adjusted) {
updateFromColor(adjusted);
}
}
void EditColorBox::prepare() {
setTitle(rpl::single(_title));
const auto hsvChanged = [=] { updateFromHSVFields(); };
const auto hsbChanged = [=] { updateFromHSBFields(); };
const auto rgbChanged = [=] { updateFromRGBFields(); };
connect(_hueField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_saturationField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_brightnessField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_hueField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_saturationField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_brightnessField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_redField, &Ui::MaskedInputField::changed, rgbChanged);
connect(_greenField, &Ui::MaskedInputField::changed, rgbChanged);
connect(_blueField, &Ui::MaskedInputField::changed, rgbChanged);
@@ -661,8 +825,15 @@ void EditColorBox::prepare() {
setDimensions(st::colorEditWidth, height);
subscribe(_picker->changed(), [=] { updateFromControls(); });
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
if (_hueSlider) {
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
}
if (_opacitySlider) {
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
}
if (_lightnessSlider) {
subscribe(_lightnessSlider->changed(), [=] { updateFromControls(); });
}
boxClosing() | rpl::start_with_next([=] {
if (_cancelCallback) {
@@ -671,7 +842,7 @@ void EditColorBox::prepare() {
}, lifetime());
updateRGBFields();
updateHSVFields();
updateHSBFields();
updateResultField();
update();
}
@@ -704,21 +875,21 @@ void EditColorBox::fieldSubmitted() {
}
void EditColorBox::saveColor() {
_cancelCallback = Fn<void()>();
const auto weak = Ui::MakeWeak(this);
_cancelCallback = nullptr;
if (_saveCallback) {
_saveCallback(_new.toRgb());
}
closeBox();
if (weak) {
closeBox();
}
}
void EditColorBox::updateHSVFields() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
_hueField->setTextWithFocus(QString::number(hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(brightness)));
void EditColorBox::updateHSBFields() {
const auto hsb = hsbFromControls();
_hueField->setTextWithFocus(QString::number(hsb.hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(hsb.saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(hsb.brightness)));
}
void EditColorBox::updateRGBFields() {
@@ -750,14 +921,28 @@ void EditColorBox::updateResultField() {
}
void EditColorBox::resizeEvent(QResizeEvent *e) {
auto fullwidth = _picker->width() + 2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width() + st::colorSampleSize.width();
const auto fullwidth = _picker->width()
+ ((_mode == Mode::RGBA)
? (2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width())
: (2 * st::colorEditSkip))
+ st::colorSampleSize.width();
auto left = (width() - fullwidth) / 2;
_picker->moveToLeft(left, st::colorEditSkip);
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
auto fieldLeft = _hueSlider->x() + _hueSlider->width() - st::colorSliderSkip + st::colorEditSkip;
auto fieldWidth = st::colorSampleSize.width();
auto fieldHeight = _hueField->height();
if (_hueSlider) {
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
}
if (_opacitySlider) {
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
}
if (_lightnessSlider) {
_lightnessSlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _lightnessSlider->height());
}
const auto fieldLeft = (_mode == Mode::RGBA)
? (_hueSlider->x() + _hueSlider->width() + st::colorEditSkip - st::colorSliderSkip)
: (_picker->x() + _picker->width() + st::colorEditSkip);
const auto addWidth = (_mode == Mode::RGBA) ? 0 : st::colorEditSkip;
const auto fieldWidth = st::colorSampleSize.width() + addWidth;
const auto fieldHeight = _hueField->height();
_newRect = QRect(fieldLeft, st::colorEditSkip, fieldWidth, st::colorSampleSize.height());
_currentRect = _newRect.translated(0, st::colorSampleSize.height());
_hueField->setGeometryToLeft(fieldLeft, _currentRect.y() + _currentRect.height() + st::colorFieldSkip, fieldWidth, fieldHeight);
@@ -766,7 +951,13 @@ void EditColorBox::resizeEvent(QResizeEvent *e) {
_redField->setGeometryToLeft(fieldLeft, _brightnessField->y() + _brightnessField->height() + st::colorFieldSkip, fieldWidth, fieldHeight);
_greenField->setGeometryToLeft(fieldLeft, _redField->y() + _redField->height(), fieldWidth, fieldHeight);
_blueField->setGeometryToLeft(fieldLeft, _greenField->y() + _greenField->height(), fieldWidth, fieldHeight);
_result->setGeometryToLeft(fieldLeft - (st::colorEditSkip + st::colorSliderWidth), _opacitySlider->y() + _opacitySlider->height() - st::colorSliderSkip - _result->height(), fieldWidth + (st::colorEditSkip + st::colorSliderWidth), fieldHeight);
const auto resultDelta = (_mode == Mode::RGBA)
? (st::colorEditSkip + st::colorSliderWidth)
: 0;
const auto resultBottom = (_mode == Mode::RGBA)
? (_opacitySlider->y() + _opacitySlider->height())
: (_lightnessSlider->y() + _lightnessSlider->height());
_result->setGeometryToLeft(fieldLeft - resultDelta, resultBottom - st::colorSliderSkip - _result->height(), fieldWidth + resultDelta, fieldHeight);
}
void EditColorBox::paintEvent(QPaintEvent *e) {
@@ -792,39 +983,74 @@ void EditColorBox::mousePressEvent(QMouseEvent *e) {
}
}
EditColorBox::HSB EditColorBox::hsbFromControls() const {
const auto hue = (_mode == Mode::RGBA)
? qRound((1. - _hueSlider->value()) * 360)
: qRound(_picker->valueX() * 360);
const auto saturation = (_mode == Mode::RGBA)
? qRound(_picker->valueX() * 255)
: qRound((1. - _picker->valueY()) * 255);
const auto brightness = (_mode == Mode::RGBA)
? qRound((1. - _picker->valueY()) * 255)
: (_lightnessMin
+ qRound(_lightnessSlider->value()
* (_lightnessMax - _lightnessMin)));
return { hue, saturation, brightness };
}
QColor EditColorBox::applyLimits(QColor color) const {
if (_mode != Mode::HSL) {
return color;
}
const auto lightness = color.lightness();
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
if (clamped == lightness) {
return color;
}
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
}
void EditColorBox::updateFromColor(QColor color) {
_new = color;
_new = applyLimits(color);
updateControlsFromColor();
updateRGBFields();
updateHSVFields();
updateHSBFields();
updateResultField();
update();
}
void EditColorBox::updateFromControls() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateHSVFields();
updateControlsFromHSV(hue, saturation, brightness);
const auto hsb = hsbFromControls();
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setHSB(hsb, alpha);
updateHSBFields();
updateControlsFromHSB(hsb);
}
void EditColorBox::updateFromHSVFields() {
auto hue = _hueField->value();
auto saturation = percentToByte(_saturationField->value());
auto brightness = percentToByte(_brightnessField->value());
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateControlsFromHSV(hue, saturation, brightness);
void EditColorBox::updateFromHSBFields() {
const auto hue = _hueField->value();
const auto saturation = percentToByte(_saturationField->value());
const auto brightness = std::clamp(
percentToByte(_brightnessField->value()),
_lightnessMin,
_lightnessMax);
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setHSB({ hue, saturation, brightness }, alpha);
updateControlsFromHSB({ hue, saturation, brightness });
}
void EditColorBox::updateFromRGBFields() {
auto red = _redField->value();
auto blue = _blueField->value();
auto green = _greenField->value();
auto alpha = qRound(_opacitySlider->value() * 255);
const auto red = _redField->value();
const auto blue = _blueField->value();
const auto green = _greenField->value();
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setRGB(red, green, blue, alpha);
updateResultField();
}
@@ -855,10 +1081,17 @@ void EditColorBox::updateFromResultField() {
updateRGBFields();
}
void EditColorBox::updateControlsFromHSV(int hue, int saturation, int brightness) {
_picker->setHSV(hue, saturation, brightness);
_hueSlider->setHSV(hue, saturation, brightness);
_opacitySlider->setHSV(hue, saturation, brightness);
void EditColorBox::updateControlsFromHSB(HSB hsb) {
_picker->setHSB(hsb);
if (_hueSlider) {
_hueSlider->setHSB(hsb);
}
if (_opacitySlider) {
_opacitySlider->setHSB(hsb);
}
if (_lightnessSlider) {
_lightnessSlider->setHSB(hsb);
}
}
void EditColorBox::updateControlsFromColor() {
@@ -867,21 +1100,32 @@ void EditColorBox::updateControlsFromColor() {
auto blue = _new.blue();
auto alpha = _new.alpha();
_picker->setRGB(red, green, blue);
_hueSlider->setRGB(red, green, blue);
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
if (_hueSlider) {
_hueSlider->setRGB(red, green, blue);
}
if (_opacitySlider) {
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
}
if (_lightnessSlider) {
_lightnessSlider->setRGB(red, green, blue);
}
}
void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
_new.setHsv(hue, saturation, value, alpha);
void EditColorBox::setHSB(HSB hsb, int alpha) {
if (_mode == Mode::RGBA) {
_new.setHsv(hsb.hue, hsb.saturation, hsb.brightness, alpha);
} else {
_new.setHsl(hsb.hue, hsb.saturation, hsb.brightness, alpha);
}
updateRGBFields();
updateResultField();
update();
}
void EditColorBox::setRGB(int red, int green, int blue, int alpha) {
_new.setRgb(red, green, blue, alpha);
_new = applyLimits(QColor(red, green, blue, alpha));
updateControlsFromColor();
updateHSVFields();
updateHSBFields();
update();
}
}

View File

@@ -9,9 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
class EditColorBox : public BoxContent {
class EditColorBox : public BoxContent, private base::Subscriber {
public:
EditColorBox(QWidget*, const QString &title, QColor current = QColor(255, 255, 255));
enum class Mode {
RGBA,
HSL,
};
EditColorBox(QWidget*, const QString &title, Mode mode, QColor current);
void setLightnessLimits(int min, int max);
void setSaveCallback(Fn<void(QColor)> callback) {
_saveCallback = std::move(callback);
@@ -36,21 +42,28 @@ protected:
void setInnerFocus() override;
private:
struct HSB { // HSV or HSL depending on Mode.
int hue = 0;
int saturation = 0;
int brightness = 0;
};
void saveColor();
void fieldSubmitted();
[[nodiscard]] HSB hsbFromControls() const;
void updateFromColor(QColor color);
void updateControlsFromColor();
void updateControlsFromHSV(int hue, int saturation, int brightness);
void updateHSVFields();
void updateControlsFromHSB(HSB hsb);
void updateHSBFields();
void updateRGBFields();
void updateResultField();
void updateFromControls();
void updateFromHSVFields();
void updateFromHSBFields();
void updateFromRGBFields();
void updateFromResultField();
void setHSV(int hue, int saturation, int brightness, int alpha);
void setHSB(HSB hsb, int alpha);
void setRGB(int red, int green, int blue, int alpha);
[[nodiscard]] QColor applyLimits(QColor color) const;
int percentFromByte(int byte) {
return snap(qRound(byte * 100 / 255.), 0, 100);
@@ -65,10 +78,12 @@ private:
class ResultField;
QString _title;
Mode _mode = Mode();
object_ptr<Picker> _picker;
object_ptr<Slider> _hueSlider;
object_ptr<Slider> _opacitySlider;
object_ptr<Slider> _hueSlider = { nullptr };
object_ptr<Slider> _opacitySlider = { nullptr };
object_ptr<Slider> _lightnessSlider = { nullptr };
object_ptr<Field> _hueField;
object_ptr<Field> _saturationField;
@@ -85,6 +100,9 @@ private:
QRect _currentRect;
QRect _newRect;
int _lightnessMin = 0;
int _lightnessMax = 255;
Fn<void(QColor)> _saveCallback;
Fn<void()> _cancelCallback;

View File

@@ -250,8 +250,9 @@ bool EditPrivacyBox::showExceptionLink(Exception exception) const {
Unexpected("Invalid exception value.");
}
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::addOption(
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::AddOption(
not_null<Ui::VerticalLayout*> container,
not_null<EditPrivacyController*> controller,
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
Option option) {
return container->add(
@@ -259,7 +260,7 @@ Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::addOption(
container,
group,
option,
_controller->optionLabel(option),
controller->optionLabel(option),
st::settingsSendType),
st::settingsSendTypePadding);
}
@@ -306,7 +307,7 @@ void EditPrivacyBox::setupContent() {
const auto addOptionRow = [&](Option option) {
return (_controller->hasOption(option) || (_value.option == option))
? addOption(content, group, option)
? AddOption(content, _controller.get(), group, option)
: nullptr;
};
const auto addExceptionLink = [=](Exception exception) {
@@ -345,7 +346,7 @@ void EditPrivacyBox::setupContent() {
auto above = _controller->setupAboveWidget(
content,
std::move(optionValue));
rpl::duplicate(optionValue));
if (above) {
content->add(std::move(above));
}
@@ -357,6 +358,14 @@ void EditPrivacyBox::setupContent() {
addLabel(content, _controller->warning());
AddSkip(content);
auto middle = _controller->setupMiddleWidget(
_window,
content,
std::move(optionValue));
if (middle) {
content->add(std::move(middle));
}
AddDivider(content);
AddSkip(content);
AddSubsectionTitle(content, tr::lng_edit_privacy_exceptions());
@@ -373,6 +382,7 @@ void EditPrivacyBox::setupContent() {
const auto someAreDisallowed = (_value.option != Option::Everyone)
|| !_value.never.empty();
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
_controller->saveAdditional();
_window->session().api().savePrivacy(
_controller->apiKey(),
collectResult());

View File

@@ -58,6 +58,12 @@ public:
rpl::producer<Option> option) {
return { nullptr };
}
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupMiddleWidget(
not_null<Window::SessionController*> controller,
not_null<QWidget*> parent,
rpl::producer<Option> option) {
return { nullptr };
}
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupBelowWidget(
not_null<Window::SessionController*> controller,
not_null<QWidget*> parent) {
@@ -69,6 +75,8 @@ public:
FnMut<void()> saveCallback) {
saveCallback();
}
virtual void saveAdditional() {
}
virtual ~EditPrivacyController() = default;
@@ -100,6 +108,12 @@ public:
std::unique_ptr<EditPrivacyController> controller,
const Value &value);
static Ui::Radioenum<Option> *AddOption(
not_null<Ui::VerticalLayout*> container,
not_null<EditPrivacyController*> controller,
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
Option option);
protected:
void prepare() override;
@@ -108,10 +122,6 @@ private:
void setupContent();
QVector<MTPInputPrivacyRule> collectResult();
Ui::Radioenum<Option> *addOption(
not_null<Ui::VerticalLayout*> container,
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
Option option);
Ui::FlatLabel *addLabel(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> text);

View File

@@ -129,7 +129,7 @@ template <std::size_t... I>
inline void GenericBox::Initer<InitMethod, InitArgs...>::call(
not_null<GenericBox*> box,
std::index_sequence<I...>) {
std::invoke(method, box, std::get<I>(args)...);
std::invoke(method, box, std::get<I>(std::move(args))...);
}
template <typename InitMethod, typename ...InitArgs>

View File

@@ -32,6 +32,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_passport.h"
#include "styles/style_chat_helpers.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
namespace {
using Language = Lang::Language;
@@ -408,7 +411,7 @@ bool Rows::hasMenu(not_null<const Row*> row) const {
void Rows::share(not_null<const Row*> row) const {
const auto link = qsl("https://t.me/setlanguage/") + row->data.id;
QApplication::clipboard()->setText(link);
QGuiApplication::clipboard()->setText(link);
Ui::Toast::Show(tr::lng_username_copied(tr::now));
}

View File

@@ -11,10 +11,11 @@ Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_peer.h"
#include "styles/style_boxes.h"
#include "ui/special_buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "app.h"
#include "styles/style_boxes.h"
namespace {
@@ -44,7 +45,7 @@ void MuteSettingsBox::prepare() {
icon->moveToLeft(st::boxPadding.left(), y);
object_ptr<Ui::FlatLabel> title(this, st::muteChatTitle);
title->setText(App::peerName(_peer, true));
title->setText(_peer->name);
title->moveToLeft(
st::boxPadding.left() + st::muteChatTitleLeft,
y + (icon->height() / 2) - (title->height() / 2));

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/fade_wrap.h"
#include "passport/passport_encryption.h"
#include "passport/passport_panel_edit_contact.h"
#include "facades.h"
#include "styles/style_boxes.h"
#include "styles/style_passport.h"
@@ -268,7 +269,7 @@ void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) {
}
_setRequest = 0;
_newPasswordSet.fire_copy(newPasswordBytes);
const auto weak = make_weak(this);
const auto weak = Ui::MakeWeak(this);
const auto text = _reenterPasscode->isHidden()
? tr::lng_cloud_password_removed(tr::now)
: _oldPasscode->isHidden()
@@ -367,7 +368,7 @@ void PasscodeBox::validateEmail(
} else if (error.type() == qstr("CODE_INVALID")) {
errors->fire(tr::lng_signin_wrong_code(tr::now));
} else if (error.type() == qstr("EMAIL_HASH_EXPIRED")) {
const auto weak = make_weak(this);
const auto weak = Ui::MakeWeak(this);
_clearUnconfirmedPassword.fire({});
if (weak) {
auto box = Box<InformBox>(
@@ -408,7 +409,7 @@ void PasscodeBox::validateEmail(
box->boxClosing(
) | rpl::filter([=] {
return !*set;
}) | start_with_next([=, weak = make_weak(this)] {
}) | start_with_next([=, weak = Ui::MakeWeak(this)] {
if (weak) {
weak->_clearUnconfirmedPassword.fire({});
}
@@ -511,7 +512,7 @@ void PasscodeBox::save(bool force) {
}
} else {
closeReplacedBy();
const auto weak = make_weak(this);
const auto weak = Ui::MakeWeak(this);
cSetPasscodeBadTries(0);
Local::setPasscode(pwd.toUtf8());
_session->localPasscodeChanged();

View File

@@ -507,14 +507,14 @@ void PeerListRow::paintDisabledCheckUserpic(
auto userpicDiameter = st::contactsPhotoCheckbox.imageRadius * 2;
auto userpicLeft = x + userpicShift;
auto userpicTop = y + userpicShift;
auto userpicEllipse = rtlrect(x, y, userpicDiameter, userpicDiameter, outerWidth);
auto userpicEllipse = style::rtlrect(x, y, userpicDiameter, userpicDiameter, outerWidth);
auto userpicBorderPen = st::contactsPhotoDisabledCheckFg->p;
userpicBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth);
auto iconDiameter = st::contactsPhotoCheckbox.check.size;
auto iconLeft = x + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter;
auto iconTop = y + userpicDiameter + st::contactsPhotoCheckbox.selectWidth - iconDiameter;
auto iconEllipse = rtlrect(iconLeft, iconTop, iconDiameter, iconDiameter, outerWidth);
auto iconEllipse = style::rtlrect(iconLeft, iconTop, iconDiameter, iconDiameter, outerWidth);
auto iconBorderPen = st::contactsPhotoCheckbox.check.border->p;
iconBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth);

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "observer_peer.h"
#include "ui/widgets/checkbox.h"
#include "ui/ui_utility.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_channel.h"
@@ -22,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "dialogs/dialogs_main_list.h"
#include "window/window_session_controller.h"
#include "facades.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
@@ -42,7 +44,8 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
MTP_string(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>()
MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
@@ -455,7 +458,7 @@ void AddBotToGroupBoxController::shareBotGame(not_null<PeerData*> chat) {
});
auto confirmText = [chat] {
if (chat->isUser()) {
return tr::lng_bot_sure_share_game(tr::now, lt_user, App::peerName(chat));
return tr::lng_bot_sure_share_game(tr::now, lt_user, chat->name);
}
return tr::lng_bot_sure_share_game_group(tr::now, lt_group, chat->name);
}();

View File

@@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
//
//};
class History;
namespace Window {
class SessionNavigation;
} // namespace Window

Some files were not shown because too many files have changed in this diff Show More