Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9c5f18142 | ||
|
|
175023b2dc | ||
|
|
0a7c42c59a | ||
|
|
42122fdea0 | ||
|
|
4bfe65d8ab | ||
|
|
1b06fe1220 | ||
|
|
1b11a7feae | ||
|
|
6b60b51775 | ||
|
|
981162e6b9 | ||
|
|
c5dd99b1f1 | ||
|
|
53c536d76d | ||
|
|
e3ab8821b9 | ||
|
|
6befea6a13 | ||
|
|
f24e3c6192 | ||
|
|
63e593b3a8 | ||
|
|
7ca4ec1bed | ||
|
|
8ed1961886 | ||
|
|
ad44e45695 | ||
|
|
962ec1e454 | ||
|
|
bcc718b5a9 | ||
|
|
ac50119dd9 | ||
|
|
e953e11b7f | ||
|
|
18361ce144 | ||
|
|
cfdacb09ac | ||
|
|
5bb83afc7a | ||
|
|
f35853c42e | ||
|
|
44492b9e2d | ||
|
|
a46bb46e54 | ||
|
|
84c190c293 | ||
|
|
9da4a21f94 | ||
|
|
2a3a351445 | ||
|
|
928cd0a8cb | ||
|
|
509d2189c1 | ||
|
|
725fa87188 | ||
|
|
147eaab59a | ||
|
|
890ec34202 | ||
|
|
92858dc7d3 | ||
|
|
4440b42848 | ||
|
|
136fd5c8e1 | ||
|
|
d92356ce28 | ||
|
|
47f673aa69 | ||
|
|
64d4a3e8a8 | ||
|
|
87e72fc7aa | ||
|
|
6d44a3ec95 | ||
|
|
eb47eabba4 | ||
|
|
cfb0de69f0 | ||
|
|
2d46cc4c11 | ||
|
|
9761c5bb56 | ||
|
|
7d6bf487a7 | ||
|
|
6fda783fc1 | ||
|
|
aeaa039542 | ||
|
|
62e85b1cf0 | ||
|
|
080a08fa76 | ||
|
|
be1eb1c693 | ||
|
|
daf5d4acdb | ||
|
|
f4781b9117 | ||
|
|
0f778431f5 | ||
|
|
2121ce1210 | ||
|
|
c54aadcac3 | ||
|
|
3fc74166de | ||
|
|
749b13adec | ||
|
|
c33ddf49ff | ||
|
|
bc1e2dcb54 | ||
|
|
9a193ed88f | ||
|
|
fb32c5bcd1 | ||
|
|
67b46d9aac | ||
|
|
aeb2ec68ef | ||
|
|
e1c304c2e5 | ||
|
|
c7de9d4668 | ||
|
|
45aa6dff75 | ||
|
|
8b0562b946 | ||
|
|
373f1a0ff0 | ||
|
|
058f6bd8de | ||
|
|
a707f7b9e7 | ||
|
|
61d1574023 | ||
|
|
fe59898e5c | ||
|
|
0df1952a04 | ||
|
|
1d8ec7c7d6 | ||
|
|
b770ea4f8d | ||
|
|
b31f2d952c | ||
|
|
370c47d95b | ||
|
|
9b3767e77c | ||
|
|
196b643e7d | ||
|
|
c9626c140c | ||
|
|
a356a4dc06 | ||
|
|
34d5c3777b | ||
|
|
ac74a08d53 | ||
|
|
47b0f901c8 | ||
|
|
18103aae75 | ||
|
|
cc9ae13297 | ||
|
|
334b3ac706 | ||
|
|
a9063eb87b | ||
|
|
175e0f71ce | ||
|
|
782c254ea0 |
@@ -52,13 +52,10 @@ Global
|
||||
{6F483617-7C84-4E7E-91D8-1FF28A4CE3A0}.Release|Win32.Build.0 = Release|Win32
|
||||
{6F483617-7C84-4E7E-91D8-1FF28A4CE3A0}.Release|x64.ActiveCfg = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Deploy|Win32.ActiveCfg = Deploy|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Deploy|Win32.Build.0 = Deploy|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Deploy|x64.ActiveCfg = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Release|Win32.Build.0 = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Release|x64.ActiveCfg = Release|Win32
|
||||
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Debug|Win32.Build.0 = Debug|Win32
|
||||
|
||||
@@ -23,7 +23,9 @@ Replace '\-lfontconfig' '\/usr\/lib\/x86_64\-linux\-gnu\/libfontconfig\.a \/usr\
|
||||
Replace '\-lfreetype' '\/usr\/lib\/x86_64\-linux\-gnu\/libfreetype\.a'
|
||||
Replace '\-lpng' '\/usr\/lib\/x86_64\-linux\-gnu\/libpng\.a'
|
||||
Replace '\-lXext' '\/usr\/lib\/x86_64\-linux\-gnu\/libXext\.a'
|
||||
Replace '\-lopusfile' '\/usr\/local\/lib\/libopusfile\.a'
|
||||
Replace '\-lopus' '\/usr\/local\/lib\/libopus\.a'
|
||||
Replace '\-lopenal' '\/usr\/local\/lib\/libopenal\.a'
|
||||
Replace '\-logg' '\/usr\/local\/lib\/libogg\.a'
|
||||
Replace '\-lavformat' '\/usr\/local\/lib\/libavformat\.a'
|
||||
Replace '\-lavcodec' '\/usr\/local\/lib\/libavcodec\.a'
|
||||
Replace '\-lswresample' '\/usr\/local\/lib\/libswresample\.a'
|
||||
Replace '\-lavutil' '\/usr\/local\/lib\/libavutil\.a'
|
||||
|
||||
@@ -515,7 +515,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
LastUpgradeCheck = 0630;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaEmoji" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -589,6 +589,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
@@ -677,6 +678,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
|
||||
@@ -516,7 +516,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
LastUpgradeCheck = 0630;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaStyle" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -603,6 +603,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
@@ -691,6 +692,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@echo OFF
|
||||
|
||||
set "AppVersion=8004"
|
||||
set "AppVersionStrSmall=0.8.4"
|
||||
set "AppVersionStr=0.8.4"
|
||||
set "AppVersionStrFull=0.8.4.0"
|
||||
set "DevChannel=0"
|
||||
set "AppVersion=8018"
|
||||
set "AppVersionStrSmall=0.8.18"
|
||||
set "AppVersionStr=0.8.18"
|
||||
set "AppVersionStrFull=0.8.18.0"
|
||||
set "DevChannel=1"
|
||||
|
||||
if %DevChannel% neq 0 goto preparedev
|
||||
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} kicked {user}";
|
||||
"lng_action_user_left" = "{from} left the group";
|
||||
"lng_action_user_joined" = "{from} joined the group";
|
||||
"lng_action_user_joined_by_link" = "{from} joined the group via invite link";
|
||||
"lng_action_user_registered" = "{from} just joined Telegram";
|
||||
"lng_action_removed_photo" = "{from} removed group photo";
|
||||
"lng_action_changed_photo" = "{from} changed group photo";
|
||||
"lng_action_changed_title" = "{from} changed group name to «{title}»";
|
||||
"lng_action_created_chat" = "{from} created group «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "This invite link is broken\nor has expired.";
|
||||
"lng_group_invite_want_join" = "Do you want to join the group «{title}»?";
|
||||
"lng_group_invite_join" = "Join";
|
||||
|
||||
"lng_group_invite_link" = "Invite link";
|
||||
"lng_group_invite_create" = "Create an invite link";
|
||||
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
||||
"lng_group_invite_create_new" = "Revoke invite link";
|
||||
"lng_group_invite_about_new" = "Your previous link will be deactivated\nand we'll generate a new invite link for you.";
|
||||
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
||||
"lng_group_invite_no_room" = "Unable to join this group because there are\ntoo many members in it already.";
|
||||
|
||||
"lng_forwarded_from" = "Forwarded from";
|
||||
"lng_in_reply_to" = "In reply to";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Video file";
|
||||
"lng_media_audio" = "Voice message";
|
||||
|
||||
"lng_emoji_category0" = "Frequently used";
|
||||
"lng_emoji_category1" = "People";
|
||||
"lng_emoji_category2" = "Nature";
|
||||
"lng_emoji_category3" = "Food & Drink";
|
||||
"lng_emoji_category4" = "Celebration";
|
||||
"lng_emoji_category5" = "Activity";
|
||||
"lng_emoji_category6" = "Travel & Places";
|
||||
"lng_emoji_category7" = "Objects & Symbols";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Custom stickers";
|
||||
"lng_stickers_remove_pack" = "Remove «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Add {count:_not_used_|# Sticker|# Stickers}";
|
||||
"lng_stickers_share_pack" = "Share Stickers";
|
||||
"lng_stickers_not_found" = "Sticker pack not found.";
|
||||
"lng_stickers_copied" = "Sticker pack link copied to clipboard.";
|
||||
"lng_stickers_default_set" = "Great Minds";
|
||||
|
||||
"lng_in_dlg_photo" = "Photo";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
"lng_in_dlg_contact" = "Contact";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Send";
|
||||
"lng_message_ph" = "Write a message..";
|
||||
"lng_record_cancel" = "Release out of here to cancel";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Please select chat to start messaging";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Save Video As..";
|
||||
"lng_context_open_audio" = "Open Audio";
|
||||
"lng_context_save_audio" = "Save Audio As..";
|
||||
"lng_context_pack_info" = "Pack Info";
|
||||
"lng_context_open_file" = "Open File";
|
||||
"lng_context_save_file" = "Save File As..";
|
||||
"lng_context_forward_file" = "Forward File";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:No messages found|Found # message|Found # messages}";
|
||||
"lng_search_global_results" = "Global search results";
|
||||
|
||||
"lng_mediaview_save" = "Download";
|
||||
"lng_media_save_progress" = "{ready} of {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Save As..";
|
||||
"lng_mediaview_copy" = "Copy";
|
||||
"lng_mediaview_forward" = "Forward";
|
||||
"lng_mediaview_delete" = "Delete";
|
||||
"lng_mediaview_photos_all" = "View all photos";
|
||||
"lng_mediaview_files_all" = "View all files";
|
||||
"lng_mediaview_single_photo" = "Single Photo";
|
||||
"lng_mediaview_group_photo" = "Group Photo";
|
||||
"lng_mediaview_profile_photo" = "Profile Photo";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} of {count}";
|
||||
"lng_mediaview_n_of_count" = "Photo {n} of {count}";
|
||||
"lng_mediaview_doc_image" = "File";
|
||||
"lng_mediaview_today" = "today at {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
|
||||
"lng_new_version_text" = "— Link previews for Twitter, YouTube, Instagram and certain other links\n— Two-step verification\n— View all your Telegram sessions, terminate specific sessions";
|
||||
"lng_new_version_text" = "— Added support for sticker packs\n— New emoji and sticker panel";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insert Unicode control character";
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ semibold: 'Open Sans Semibold';
|
||||
fsize: 13px;
|
||||
|
||||
spriteFile: ':/gui/art/sprite.png' / 2:':/gui/art/sprite_125x.png' / 3:':/gui/art/sprite_150x.png' / 4:':/gui/art/sprite_200x.png';
|
||||
emojisFile: ':/gui/art/emoji.png' / 2:':/gui/art/emoji_125x.png' / 3:':/gui/art/emoji_150x.png' / 4:':/gui/art/emoji_200x.png';
|
||||
emojiImgSize: 18px; // exceptional value for retina
|
||||
emojiSize: 18px;
|
||||
emojiPadding: 0px;
|
||||
@@ -55,7 +54,7 @@ wndBG: #FFF;
|
||||
wndShadow: sprite(209px, 46px, 19px, 19px);
|
||||
wndShadowShift: 1px;
|
||||
|
||||
layerAlpha: 0.3;
|
||||
layerAlpha: 0.5;
|
||||
layerBG: #000;
|
||||
|
||||
titleBG: #6389a8;
|
||||
@@ -123,9 +122,9 @@ sysUnlock: sysButton(sysUpd) {
|
||||
img: sprite(207px, 22px, 19px, 19px);
|
||||
}
|
||||
titleBackButton: iconedButton(btnDefIconed) {
|
||||
icon: sprite(133px, 197px, 13px, 20px);
|
||||
icon: sprite(113px, 108px, 13px, 20px);
|
||||
iconPos: point(5px, 9px);
|
||||
downIcon: sprite(133px, 197px, 13px, 20px);
|
||||
downIcon: sprite(113px, 108px, 13px, 20px);
|
||||
downIconPos: point(5px, 10px);
|
||||
|
||||
bgColor: #c4d8e9;
|
||||
@@ -309,7 +308,8 @@ scrollDef: flatScroll {
|
||||
width: 10px;
|
||||
minHeight: 20px;
|
||||
deltax: 3px;
|
||||
deltay: 3px;
|
||||
deltat: 3px;
|
||||
deltab: 3px;
|
||||
|
||||
topsh: 2px;
|
||||
bottomsh: 2px;
|
||||
@@ -390,7 +390,8 @@ btnIntroNext: flatButton(btnDefNext, btnDefBig) {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
boxShadow: sprite(230px, 46px, 9px, 9px);
|
||||
boxShadow: sprite(363px, 50px, 15px, 15px);
|
||||
boxShadowShift: 2px;
|
||||
|
||||
introCountry: countryInput {
|
||||
width: 300px;
|
||||
@@ -699,6 +700,7 @@ dlgDateSkip: 5px;
|
||||
|
||||
dlgUnreadColor: #FFF;
|
||||
dlgUnreadBG: #6fc766;
|
||||
dlgUnreadMutedBG: #bbb;
|
||||
dlgUnreadFont: font(12px bold);
|
||||
dlgUnreadPaddingHor: 5px;
|
||||
dlgUnreadPaddingVer: 1px;
|
||||
@@ -773,7 +775,9 @@ historyToEndSkip: 10px;
|
||||
activeFadeInDuration: 500;
|
||||
activeFadeOutDuration: 3000;
|
||||
|
||||
msgMaxWidth: 550px;
|
||||
msgRadius: 3px;
|
||||
|
||||
msgMaxWidth: 430px;
|
||||
msgFont: font(fsize);
|
||||
msgNameFont: font(fsize semibold);
|
||||
msgServiceFont: font(fsize semibold);
|
||||
@@ -787,12 +791,11 @@ msgPadding: margins(13px, 7px, 13px, 8px);
|
||||
msgMargin: margins(13px, 4px, 53px, 4px);
|
||||
msgLnkPadding: 2px; // for media open / save links
|
||||
msgBorder: #f0f0f0;
|
||||
msgOutBG: #effdde;
|
||||
msgInBG: #fff;
|
||||
msgOutSelectBG: #b7dbdb;
|
||||
msgInSelectBG: #c2dcf2; // #358cd4 with 30% opacity
|
||||
msgOutSelectOverlay: #358cd44c;
|
||||
msgInSelectOverlay: #358cd44c;
|
||||
msgOutBg: #effdde;
|
||||
msgInBg: #fff;
|
||||
msgOutSelectBg: #b7dbdb;
|
||||
msgInSelectBg: #c2dcf2; // #358cd4 with 30% opacity
|
||||
msgSelectOverlay: #358cd44c;
|
||||
msgStickerOverlay: #358cd47f;
|
||||
msgOutServiceColor: #3a8e26;
|
||||
msgInServiceColor: #0e7acd;
|
||||
@@ -817,10 +820,8 @@ msgInReplyBarColor: #2fa9e2;
|
||||
msgOutReplyBarSelColor: #4da79f;
|
||||
msgInReplyBarSelColor: #2fa9e2;
|
||||
|
||||
msgServiceSelectBG: #fff4;
|
||||
msgServiceRadius: 2px;
|
||||
|
||||
msgServiceBG: #89a0b47f;
|
||||
msgServiceBg: #89a0b47f;
|
||||
msgServiceSelectBg: #bbc8d4a2;
|
||||
msgServiceColor: #FFF;
|
||||
msgServicePadding: margins(12px, 3px, 12px, 4px);
|
||||
msgServiceMargin: margins(10px, 7px, 80px, 7px);
|
||||
@@ -831,7 +832,8 @@ msgLinkColor: #2a6dc2;
|
||||
msgPressedLinkColor: #004bad;
|
||||
msgSkip: 40px;
|
||||
msgPtr: 8px;
|
||||
msgBG: ':/gui/art/bg.png' / 2:':/gui/art/bg_125x.png' / 3:':/gui/art/bg_150x.png' / 4:':/gui/art/bg_200x.png';
|
||||
msgBG: ':/gui/art/bg.jpg';
|
||||
msgBG0: ':/gui/art/bg0.png';
|
||||
|
||||
msgSendingRect: sprite(260px, 20px, 20px, 20px);
|
||||
msgCheckRect: sprite(320px, 0px, 20px, 20px);
|
||||
@@ -849,6 +851,7 @@ msgImgDblCheckRect: sprite(300px, 65px, 20px, 20px);
|
||||
msgDateImgDelta: 4px;
|
||||
msgDateImgColor: #fff;
|
||||
msgDateImgBg: #00000054;
|
||||
msgDateImgSelectBg: #1c4a7187;
|
||||
msgDateImgPadding: point(8px, 2px);
|
||||
msgDateImgCheckSpace: 4px;
|
||||
|
||||
@@ -860,23 +863,23 @@ defaultTextStyle: textStyle {
|
||||
lnkOverFlags: font(fsize underline);
|
||||
lnkColor: btnYesColor;
|
||||
lnkDownColor: btnYesHover;
|
||||
selectBG: msgInSelectBG;
|
||||
selectOverlay: msgInSelectOverlay;
|
||||
selectBg: msgInSelectBg;
|
||||
selectOverlay: msgSelectOverlay;
|
||||
lineHeight: 0px;
|
||||
}
|
||||
serviceTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: msgServiceColor;
|
||||
lnkDownColor: msgServiceColor;
|
||||
selectBG: msgServiceSelectBG;
|
||||
selectOverlay: msgServiceSelectBG;
|
||||
selectBg: msgServiceSelectBg;
|
||||
selectOverlay: msgServiceSelectBg;
|
||||
}
|
||||
inTextStyle: textStyle(defaultTextStyle) {
|
||||
selectBG: msgInSelectBG;
|
||||
selectOverlay: msgInSelectOverlay;
|
||||
selectBg: msgInSelectBg;
|
||||
selectOverlay: msgSelectOverlay;
|
||||
}
|
||||
outTextStyle: textStyle(defaultTextStyle) {
|
||||
selectBG: msgOutSelectBG;
|
||||
selectOverlay: msgOutSelectOverlay;
|
||||
selectBg: msgOutSelectBg;
|
||||
selectOverlay: msgSelectOverlay;
|
||||
}
|
||||
medviewSaveAsTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: #91d9ff;
|
||||
@@ -918,6 +921,12 @@ mediaInColor: msgInDateColor;
|
||||
mediaOutColor: msgOutDateColor;
|
||||
mediaInSelectColor: msgInSelectDateColor;
|
||||
mediaOutSelectColor: msgOutSelectDateColor;
|
||||
mediaOutUnreadColor: #6aad60;
|
||||
mediaOutUnreadSelectColor: #5aa382;
|
||||
mediaInUnreadColor: #999;
|
||||
mediaInUnreadSelectColor: #7b95aa;
|
||||
mediaUnreadSize: 4px;
|
||||
mediaUnreadSkip: 5px;
|
||||
mediaSaveDelta: 14px; // between bubble and download
|
||||
mediaSaveButton: flatButton(btnDefFlat) {
|
||||
color: #507da2;
|
||||
@@ -976,13 +985,22 @@ btnAttachPhoto: iconedButton(btnAttachDocument) {
|
||||
}
|
||||
btnAttachEmoji: iconedButton(btnAttachDocument) {
|
||||
overBgColor: white;
|
||||
icon: sprite(311px, 221px, 20px, 20px);
|
||||
icon: sprite(363px, 344px, 21px, 22px);
|
||||
iconPos: point(6px, 13px);
|
||||
downIcon: sprite(311px, 221px, 20px, 20px);
|
||||
downIcon: sprite(363px, 344px, 21px, 22px);
|
||||
downIconPos: point(6px, 13px);
|
||||
|
||||
width: 32px;
|
||||
width: 33px;
|
||||
}
|
||||
btnRecordAudio: sprite(363px, 366px, 16px, 24px);
|
||||
btnRecordAudioActive: sprite(379px, 366px, 16px, 24px);
|
||||
recordSignalColor: #f17077;
|
||||
recordSignalMin: 5px;
|
||||
recordSignalMax: 10px;
|
||||
recordCancel: #aaa;
|
||||
recordCancelActive: #ec6466;
|
||||
recordFont: font(16px);
|
||||
recordTextTop: 12px;
|
||||
|
||||
replySkip: 51px;
|
||||
replyColor: #377aae;
|
||||
@@ -990,7 +1008,7 @@ replyHeight: 49px;
|
||||
replyTop: 8px;
|
||||
replyBottom: 6px;
|
||||
replyIconPos: point(13px, 13px);
|
||||
replyIcon: sprite(174px, 195px, 24px, 24px);
|
||||
replyIcon: sprite(343px, 197px, 24px, 24px);
|
||||
replyCancel: iconedButton(btnDefIconed) {
|
||||
icon: sprite(165px, 24px, 14px, 14px);
|
||||
iconPos: point(17px, 17px);
|
||||
@@ -1001,7 +1019,7 @@ replyCancel: iconedButton(btnDefIconed) {
|
||||
width: 49px;
|
||||
height: 49px;
|
||||
}
|
||||
forwardIcon: sprite(368px, 173px, 24px, 24px);
|
||||
forwardIcon: sprite(368px, 197px, 24px, 24px);
|
||||
|
||||
historyScroll: flatScroll(scrollDef) {
|
||||
barColor: #89a0b47a;
|
||||
@@ -1013,7 +1031,8 @@ historyScroll: flatScroll(scrollDef) {
|
||||
|
||||
width: 12px;
|
||||
deltax: 3px;
|
||||
deltay: 3px;
|
||||
deltat: 3px;
|
||||
deltab: 3px;
|
||||
|
||||
topsh: 0px;
|
||||
bottomsh: -1px;
|
||||
@@ -1168,10 +1187,9 @@ btnShareContact: flatButton(btnDefNext, btnDefBig) {
|
||||
}
|
||||
|
||||
forwardWidth: 364px;
|
||||
forwardRadius: 2px;
|
||||
forwardMargins: margins(30px, 10px, 30px, 10px);
|
||||
forwardFont: font(16px);
|
||||
forwardBG: rgba(0, 0, 0, 76);
|
||||
forwardBg: rgba(0, 0, 0, 76);
|
||||
btnProfileCancel: flatButton(btnDefFlat, btnDefBig) {
|
||||
color: #666d78;
|
||||
overColor: #666d78;
|
||||
@@ -1355,18 +1373,23 @@ btnInfoClose: flatButton(aboutCloseButton) {
|
||||
emojiTextFont: font(16px);
|
||||
emojiReplaceWidth: 56px;
|
||||
emojiReplaceHeight: 56px;
|
||||
emojiReplaceInnerHeight: 38px;
|
||||
emojiReplaceInnerHeight: 42px;
|
||||
|
||||
connectingBG: #fffe;
|
||||
connectingColor: #777;
|
||||
connectingPadding: margins(5px, 5px, 5px, 5px);
|
||||
|
||||
dropdownPadding: margins(10px, 10px, 10px, 10px);
|
||||
dropdownShadow: sprite(241px, 46px, 6px, 6px);
|
||||
dropdownBorder: 1px;
|
||||
dropdownBorderColor: #ebebeb;
|
||||
dropdownBackground: white;
|
||||
dropdownDuration: 150;
|
||||
dropdownDef: dropdown {
|
||||
border: 1px;
|
||||
borderColor: #ebebeb;
|
||||
|
||||
padding: margins(10px, 10px, 10px, 10px);
|
||||
shadow: sprite(241px, 46px, 6px, 6px);
|
||||
shadowShift: 1px;
|
||||
|
||||
duration: 150;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
dropdownAttachDocument: iconedButton(btnAttachDocument) {
|
||||
iconPos: point(14px, 13px);
|
||||
@@ -1441,41 +1464,90 @@ dpiFont2: linkFont;
|
||||
dpiFont3: linkFont;
|
||||
dpiFont4: linkFont;
|
||||
|
||||
emojiScroll: flatScroll(scrollDef) {
|
||||
width: 5px;
|
||||
deltax: 2px;
|
||||
deltay: 1px;
|
||||
newScroll: flatScroll(scrollDef) {
|
||||
barColor: #3f729734;
|
||||
bgColor: #214f751a;
|
||||
barOverColor: #3f729734;
|
||||
bgOverColor: #214f751a;
|
||||
|
||||
deltax: 5px;
|
||||
width: 14px;
|
||||
deltat: 6px;
|
||||
deltab: 6px;
|
||||
|
||||
topsh: 0px;
|
||||
bottomsh: 0px;
|
||||
|
||||
hiding: 0;
|
||||
}
|
||||
emojiRecentActive: sprite(290px, 287px, 20px, 20px);
|
||||
emojiRecentOver: sprite(311px, 287px, 20px, 20px);
|
||||
emojiRecent: sprite(6px, 197px, 20px, 20px);
|
||||
emojiPeopleActive: sprite(290px, 221px, 20px, 20px);
|
||||
emojiPeopleOver: sprite(311px, 221px, 20px, 20px);
|
||||
emojiPeople: sprite(27px, 197px, 20px, 20px);
|
||||
emojiNatureActive: sprite(245px, 266px, 20px, 20px);
|
||||
emojiNatureOver: sprite(266px, 266px, 20px, 20px);
|
||||
emojiNature: sprite(48px, 197px, 20px, 20px);
|
||||
emojiObjectsActive: sprite(290px, 242px, 20px, 20px);
|
||||
emojiObjectsOver: sprite(311px, 242px, 20px, 20px);
|
||||
emojiObjects: sprite(69px, 197px, 20px, 20px);
|
||||
emojiPlacesActive: sprite(245px, 287px, 20px, 20px);
|
||||
emojiPlacesOver: sprite(266px, 287px, 20px, 20px);
|
||||
emojiPlaces: sprite(90px, 197px, 20px, 20px);
|
||||
emojiSymbolsActive: sprite(290px, 266px, 20px, 20px);
|
||||
emojiSymbolsOver: sprite(311px, 266px, 20px, 20px);
|
||||
emojiSymbols: sprite(111px, 197px, 20px, 20px);
|
||||
emojiStickersActive: sprite(311px, 308px, 20px, 20px);
|
||||
emojiStickersOver: sprite(354px, 200px, 20px, 20px);
|
||||
emojiStickers: sprite(375px, 200px, 20px, 20px);
|
||||
|
||||
stickersMaxHeight: 340px;
|
||||
stickersAddOrShare: 70px;
|
||||
btnStickersAdd: flatButton(btnDefNext, btnDefBig) {
|
||||
width: 200px;
|
||||
height: 42px;
|
||||
|
||||
textTop: 9px;
|
||||
overTextTop: 9px;
|
||||
downTextTop: 10px;
|
||||
|
||||
font: font(17px);
|
||||
overFont: font(17px);
|
||||
|
||||
bgColor: #15c23c;
|
||||
overBgColor: #13a835;
|
||||
downBgColor: #13a835;
|
||||
}
|
||||
btnStickersClose: iconedButton(notifyClose) {
|
||||
iconPos: point(21px, 21px);
|
||||
downIconPos: point(21px, 22px);
|
||||
width: 52px;
|
||||
height: 48px;
|
||||
}
|
||||
stickersWidth: 344px;
|
||||
stickersPadding: 10px;
|
||||
stickersSize: size(64px, 64px);
|
||||
stickersScroll: flatScroll(newScroll) {
|
||||
deltab: 76px;
|
||||
}
|
||||
|
||||
emojiScroll: flatScroll(newScroll) {
|
||||
deltat: 48px;
|
||||
}
|
||||
emojiRecent: sprite(0px, 196px, 21px, 22px);
|
||||
emojiRecentOver: sprite(287px, 220px, 21px, 22px);
|
||||
emojiRecentActive: sprite(287px, 242px, 21px, 22px);
|
||||
emojiPeople: sprite(21px, 196px, 21px, 22px);
|
||||
emojiPeopleOver: sprite(308px, 220px, 21px, 22px);
|
||||
emojiPeopleActive: sprite(308px, 242px, 21px, 22px);
|
||||
emojiNature: sprite(42px, 196px, 21px, 22px);
|
||||
emojiNatureOver: sprite(245px, 264px, 21px, 22px);
|
||||
emojiNatureActive: sprite(245px, 286px, 21px, 22px);
|
||||
emojiFood: sprite(63px, 196px, 21px, 22px);
|
||||
emojiFoodOver: sprite(266px, 264px, 21px, 22px);
|
||||
emojiFoodActive: sprite(266px, 286px, 21px, 22px);
|
||||
emojiCelebration: sprite(84px, 196px, 21px, 22px);
|
||||
emojiCelebrationOver: sprite(287px, 264px, 21px, 22px);
|
||||
emojiCelebrationActive: sprite(287px, 286px, 21px, 22px);
|
||||
emojiActivity: sprite(126px, 196px, 21px, 22px);
|
||||
emojiActivityOver: sprite(321px, 344px, 21px, 22px);
|
||||
emojiActivityActive: sprite(321px, 366px, 21px, 22px);
|
||||
emojiTravel: sprite(105px, 196px, 21px, 22px);
|
||||
emojiTravelOver: sprite(308px, 264px, 21px, 22px);
|
||||
emojiTravelActive: sprite(308px, 286px, 21px, 22px);
|
||||
emojiObjects: sprite(147px, 196px, 21px, 22px);
|
||||
emojiObjectsOver: sprite(342px, 344px, 21px, 22px);
|
||||
emojiObjectsActive: sprite(342px, 366px, 21px, 22px);
|
||||
|
||||
emojiPanCategories: #f7f7f7;
|
||||
|
||||
rbEmoji: flatCheckbox {
|
||||
textColor: transparent;
|
||||
bgColor: transparent;
|
||||
disColor: transparent;
|
||||
bgColor: emojiPanCategories;
|
||||
disColor: emojiPanCategories;
|
||||
|
||||
width: 29px;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
height: 46px;
|
||||
|
||||
textTop: 0px;
|
||||
textLeft: 0px;
|
||||
@@ -1485,7 +1557,7 @@ rbEmoji: flatCheckbox {
|
||||
cursor: cursor(pointer);
|
||||
|
||||
disabledCursor: cursor(default);
|
||||
imagePos: point(5px, 8px);
|
||||
imagePos: point(8px, 12px);
|
||||
}
|
||||
rbEmojiRecent: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiRecent;
|
||||
@@ -1511,6 +1583,38 @@ rbEmojiNature: flatCheckbox(rbEmoji) {
|
||||
disImageRect: emojiNature;
|
||||
chkDisImageRect: emojiNatureActive;
|
||||
}
|
||||
rbEmojiFood: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiFood;
|
||||
chkImageRect: emojiFoodActive;
|
||||
overImageRect: emojiFoodOver;
|
||||
chkOverImageRect: emojiFoodActive;
|
||||
disImageRect: emojiFood;
|
||||
chkDisImageRect: emojiFoodActive;
|
||||
}
|
||||
rbEmojiCelebration: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiCelebration;
|
||||
chkImageRect: emojiCelebrationActive;
|
||||
overImageRect: emojiCelebrationOver;
|
||||
chkOverImageRect: emojiCelebrationActive;
|
||||
disImageRect: emojiCelebration;
|
||||
chkDisImageRect: emojiCelebrationActive;
|
||||
}
|
||||
rbEmojiActivity: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiActivity;
|
||||
chkImageRect: emojiActivityActive;
|
||||
overImageRect: emojiActivityOver;
|
||||
chkOverImageRect: emojiActivityActive;
|
||||
disImageRect: emojiActivity;
|
||||
chkDisImageRect: emojiActivityActive;
|
||||
}
|
||||
rbEmojiTravel: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiTravel;
|
||||
chkImageRect: emojiTravelActive;
|
||||
overImageRect: emojiTravelOver;
|
||||
chkOverImageRect: emojiTravelActive;
|
||||
disImageRect: emojiTravel;
|
||||
chkDisImageRect: emojiTravelActive;
|
||||
}
|
||||
rbEmojiObjects: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiObjects;
|
||||
chkImageRect: emojiObjectsActive;
|
||||
@@ -1519,146 +1623,137 @@ rbEmojiObjects: flatCheckbox(rbEmoji) {
|
||||
disImageRect: emojiObjects;
|
||||
chkDisImageRect: emojiObjectsActive;
|
||||
}
|
||||
rbEmojiPlaces: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiPlaces;
|
||||
chkImageRect: emojiPlacesActive;
|
||||
overImageRect: emojiPlacesOver;
|
||||
chkOverImageRect: emojiPlacesActive;
|
||||
disImageRect: emojiPlaces;
|
||||
chkDisImageRect: emojiPlacesActive;
|
||||
}
|
||||
rbEmojiSymbols: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiSymbols;
|
||||
chkImageRect: emojiSymbolsActive;
|
||||
overImageRect: emojiSymbolsOver;
|
||||
chkOverImageRect: emojiSymbolsActive;
|
||||
disImageRect: emojiSymbols;
|
||||
chkDisImageRect: emojiSymbolsActive;
|
||||
}
|
||||
rbEmojiStickers: flatCheckbox(rbEmojiRecent) {
|
||||
imageRect: emojiStickers;
|
||||
chkImageRect: emojiStickersActive;
|
||||
overImageRect: emojiStickersOver;
|
||||
chkOverImageRect: emojiStickersActive;
|
||||
disImageRect: emojiStickers;
|
||||
chkDisImageRect: emojiStickersActive;
|
||||
}
|
||||
emojiPanPadding: margins(5px, 0px, 0px, 5px);
|
||||
emojiPanSize: size(28px, 28px);
|
||||
emojiPanSub: 0px;
|
||||
emojiPanPadding: 10px;
|
||||
emojiPanSize: size(39px, 35px);
|
||||
emojiPanFullSize: size(300px, 321px);
|
||||
emojiPanDuration: 200;
|
||||
emojiPanHover: #f0f0f0;
|
||||
emojiPanRound: 2px;
|
||||
emojiPanHover: #f0f4f7;
|
||||
|
||||
stickerPanRound: 3px;
|
||||
stickerPanPadding: 2px;
|
||||
stickerPanDelete: sprite(158px, 197px, 12px, 12px);
|
||||
emojiPanHeader: 42px;
|
||||
emojiPanHeaderFont: font(fsize semibold);
|
||||
emojiPanHeaderColor: #999;
|
||||
emojiPanHeaderLeft: 17px;
|
||||
emojiPanHeaderTop: 12px;
|
||||
emojiPanHeaderBg: #fffffff2;
|
||||
|
||||
emojiColorsPadding: 5px;
|
||||
emojiColorsSep: 1px;
|
||||
emojiColorsSepColor: #d5d5d5;
|
||||
|
||||
emojiSwitchSkip: 27px;
|
||||
emojiSwitchImgSkip: 21px;
|
||||
emojiSwitchStickers: sprite(318px, 328px, 8px, 12px);
|
||||
emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px);
|
||||
emojiSwitchColor: #42a8db;
|
||||
|
||||
stickerPanSize: size(55px, 55px);
|
||||
stickerPanPadding: 11px;
|
||||
stickerPanDelete: sprite(123px, 132px, 12px, 12px);
|
||||
stickerPanDeleteOpacity: 0.5;
|
||||
|
||||
medviewNavBarWidth: 132px;
|
||||
medviewLightNav: 0.5;
|
||||
medviewDarkNav: 1;
|
||||
medviewHeaderFont: font(semibold 18px);
|
||||
medviewNameFont: font(16px);
|
||||
medviewDateFont: font(14px);
|
||||
medviewNameTop: 13px;
|
||||
medviewDateTop: 39px;
|
||||
medviewLeft: sprite(340px, 79px, 28px, 48px);
|
||||
medviewRight: sprite(368px, 79px, 28px, 48px);
|
||||
medviewDeltaFromLastAction: 5px;
|
||||
medviewSwipeDistance: 80px;
|
||||
mvBgColor: #222;
|
||||
mvBgOpacity: 0.92;
|
||||
mvThickFont: font(fsize semibold);
|
||||
mvFont: font(fsize);
|
||||
|
||||
medviewSaveMsgCheck: sprite(341px, 174px, 22px, 18px);
|
||||
mvTextLeft: 16px;
|
||||
mvTextSkip: 10px;
|
||||
mvHeaderTop: 48px;
|
||||
mvTextTop: 24px;
|
||||
mvTextColor: white;
|
||||
mvTextOpacity: 0.5;
|
||||
mvTextOverOpacity: 1;
|
||||
|
||||
mvIconOpacity: 0.45;
|
||||
mvIconOverOpacity: 1;
|
||||
mvControlBgColor: black;
|
||||
mvControlBgOpacity: 0.3;
|
||||
mvControlMargin: 0px;
|
||||
mvControlSize: 90px;
|
||||
mvIconSize: size(60px, 56px);
|
||||
|
||||
mvLeft: sprite(320px, 400px, 12px, 22px);
|
||||
mvRight: sprite(332px, 400px, 12px, 22px);
|
||||
mvClose: sprite(344px, 400px, 18px, 18px);
|
||||
mvSave: sprite(362px, 400px, 14px, 19px);
|
||||
mvMore: sprite(376px, 400px, 5px, 21px);
|
||||
|
||||
mvDropdown: dropdown(dropdownDef) {
|
||||
shadow: sprite(0px, 0px, 0px, 0px);
|
||||
padding: margins(11px, 12px, 11px, 12px);
|
||||
|
||||
border: 0;
|
||||
width: 182px;
|
||||
}
|
||||
mvButton: iconedButton(btnDefIconed) {
|
||||
bgColor: #383838;
|
||||
overBgColor: #505050;
|
||||
font: font(fsize);
|
||||
|
||||
opacity: 1;
|
||||
overOpacity: 1;
|
||||
|
||||
width: -32px;
|
||||
height: 36px;
|
||||
|
||||
color: white;
|
||||
|
||||
textPos: point(16px, 9px);
|
||||
downTextPos: point(16px, 10px);
|
||||
|
||||
duration: 0;
|
||||
}
|
||||
mvContextButton: iconedButton(mvButton) {
|
||||
bgColor: #383838E6;
|
||||
overBgColor: #505050E7;
|
||||
}
|
||||
mvWaitHide: 2000;
|
||||
mvHideDuration: 1000;
|
||||
mvShowDuration: 200;
|
||||
mvFadeDuration: 150;
|
||||
|
||||
mvDocPadding: 18px;
|
||||
mvDocSize: size(340px, 116px);
|
||||
mvDocBg: white;
|
||||
mvDocNameTop: 4px;
|
||||
mvDocNameFont: font(semibold 14px);
|
||||
mvDocNameColor: black;
|
||||
mvDocSizeTop: 29px;
|
||||
mvDocSizeColor: #808080;
|
||||
mvDocExtTop: 35px;
|
||||
mvDocExtFont: font(semibold 18px);
|
||||
mvDocExtColor: white;
|
||||
mvDocExtPadding: 10px;
|
||||
mvDocLinksTop: 57px;
|
||||
mvDocRed: sprite(0px, 400px, 80px, 80px);
|
||||
mvDocYellow: sprite(80px, 400px, 80px, 80px);
|
||||
mvDocGreen: sprite(160px, 400px, 80px, 80px);
|
||||
mvDocBlue: sprite(240px, 400px, 80px, 80px);
|
||||
mvDocLink: linkButton(btnDefLink) {
|
||||
color: #4595d3;
|
||||
overColor: #4595d3;
|
||||
downColor: #4595d3;
|
||||
}
|
||||
|
||||
mvDeltaFromLastAction: 5px;
|
||||
mvSwipeDistance: 80px;
|
||||
|
||||
mvCaptionPadding: margins(18px, 10px, 18px, 10px);
|
||||
mvCaptionMargin: size(11px, 11px);
|
||||
mvCaptionRadius: 2px;
|
||||
mvCaptionBg: #11111180;
|
||||
mvCaptionFont: font(fsize);
|
||||
|
||||
medviewSaveMsgCheck: sprite(311px, 309px, 22px, 18px);
|
||||
medviewSaveMsgFont: font(16px);
|
||||
medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px);
|
||||
medviewSaveMsgCheckPos: point(23px, 21px);
|
||||
medviewSaveMsgRadius: 3px;
|
||||
medviewSaveMsgShowing: 200;
|
||||
medviewSaveMsgShown: 2000;
|
||||
medviewSaveMsgHiding: 2500;
|
||||
medviewSaveMsg: #000000b2;
|
||||
|
||||
medviewOverview: iconedButton(btnDefIconed) {
|
||||
bgColor: #0000;
|
||||
overBgColor: #00000040;
|
||||
font: font(16px);
|
||||
|
||||
opacity: 0.77;
|
||||
overOpacity: 1;
|
||||
|
||||
icon: sprite(340px, 129px, 19px, 19px);
|
||||
iconPos: point(16px, 14px);
|
||||
downIcon: sprite(340px, 129px, 19px, 19px);
|
||||
downIconPos: point(16px, 14px);
|
||||
|
||||
width: -69px;
|
||||
height: 47px;
|
||||
|
||||
color: white;
|
||||
|
||||
textPos: point(51px, 13px);
|
||||
downTextPos: point(51px, 14px);
|
||||
}
|
||||
medviewForward: iconedButton(medviewOverview) {
|
||||
icon: sprite(357px, 58px, 22px, 17px);
|
||||
iconPos: point(16px, 15px);
|
||||
downIcon: sprite(357px, 58px, 22px, 17px);
|
||||
downIconPos: point(16px, 15px);
|
||||
|
||||
width: -69px;
|
||||
}
|
||||
medviewDelete: iconedButton(medviewForward) {
|
||||
icon: sprite(340px, 58px, 15px, 19px);
|
||||
iconPos: point(16px, 14px);
|
||||
downIcon: sprite(340px, 58px, 15px, 19px);
|
||||
downIconPos: point(16px, 14px);
|
||||
}
|
||||
medviewClose: iconedButton(medviewOverview) {
|
||||
icon: sprite(340px, 0px, 56px, 56px);
|
||||
iconPos: point(0px, 0px);
|
||||
downIcon: sprite(340px, 0px, 56px, 56px);
|
||||
downIconPos: point(0px, 0px);
|
||||
|
||||
opacity: 0.6;
|
||||
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
medviewBottomBar: 87px;
|
||||
medviewBG: #272727D9;
|
||||
medviewBottomBG: #272727;
|
||||
medviewNavOpacity: 0.6;
|
||||
medviewCloseOpacity: 0.6;
|
||||
medviewNavBGOpacity: 0.4;
|
||||
medviewNavOverOpacity: 1;
|
||||
medviewCloseOverOpacity: 1;
|
||||
medviewNameColor: black;
|
||||
medviewDateColor: #999;
|
||||
medviewSaveAs: iconedButton(medviewOverview) {
|
||||
bgColor: #38abe6;
|
||||
overBgColor: #299fdc;
|
||||
|
||||
opacity: 1;
|
||||
|
||||
icon: sprite(361px, 129px, 12px, 19px);
|
||||
iconPos: point(18px, 15px);
|
||||
downIcon: sprite(361px, 129px, 12px, 19px);
|
||||
downIconPos: point(18px, 15px);
|
||||
|
||||
width: -62px;
|
||||
height: 47px;
|
||||
|
||||
textPos: point(44px, 13px);
|
||||
downTextPos: point(44px, 14px);
|
||||
}
|
||||
medviewSaveAsDisabledOpacity: 0.8;
|
||||
medviewPolaroid: margins(17px, 18px, 17px, 72px);
|
||||
medviewPolaroidMin: size(480px, 360px);
|
||||
medviewDocumentSprite: sprite(341px, 150px, 20px, 22px);
|
||||
medviewDocumentSpritePos: point(16px, 13px);
|
||||
medviewPhotoSprite: sprite(363px, 150px, 23px, 20px);
|
||||
medviewPhotoSpritePos: point(14px, 14px);
|
||||
medviewTransparentBrush: sprite(148px, 197px, 8px, 8px);
|
||||
mvTransparentBrush: sprite(113px, 128px, 8px, 8px);
|
||||
|
||||
overviewPhotoSkip: 10px;
|
||||
overviewPhotoMinSize: 100px;
|
||||
@@ -1704,6 +1799,16 @@ photoLoaderDuration1: 150; // ms fade in
|
||||
photoLoaderDuration2: 150; // ms fade out
|
||||
photoLoaderAlphaMin: 0.1; // not less than that
|
||||
|
||||
radialSize: size(50px, 50px);
|
||||
radialLine: 2px;
|
||||
radialDuration: 350;
|
||||
radialPeriod: 3000;
|
||||
radialBgOpacity: 0.4;
|
||||
radialDownload: sprite(346px, 0px, 50px, 50px);
|
||||
radialDownloadOpacity: 0.8;
|
||||
radialCancel: sprite(378px, 50px, 18px, 18px);
|
||||
radialCancelOpacity: 1.0;
|
||||
|
||||
overviewLoader: size(34px, 14px);
|
||||
overviewLoaderPoint: size(4px, 4px);
|
||||
overviewLoaderSkip: 4px;
|
||||
|
||||
@@ -20,7 +20,7 @@ textStyle {
|
||||
lnkOverFlags: font;
|
||||
lnkColor: color;
|
||||
lnkDownColor: color;
|
||||
selectBG: color;
|
||||
selectBg: color;
|
||||
selectOverlay: color;
|
||||
lineHeight: number;
|
||||
}
|
||||
@@ -173,7 +173,8 @@ flatScroll {
|
||||
width: number;
|
||||
minHeight: number;
|
||||
deltax: number;
|
||||
deltay: number;
|
||||
deltat: number;
|
||||
deltab: number;
|
||||
|
||||
topsh: number;
|
||||
bottomsh: number;
|
||||
@@ -246,3 +247,15 @@ switcher {
|
||||
|
||||
duration: number;
|
||||
}
|
||||
|
||||
dropdown {
|
||||
border: number;
|
||||
borderColor: color;
|
||||
|
||||
padding: margins;
|
||||
shadow: sprite;
|
||||
shadowShift: number;
|
||||
|
||||
duration: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
@@ -448,6 +448,7 @@ static const char *variantNames[] = { "dbisOne", "dbisOneAndQuarter", "dbisOneAn
|
||||
static const char *variantPostfixes[] = { "", "_125x", "_150x", "_200x" };
|
||||
QPixmap *spriteMax = 0;
|
||||
QImage *variantSprites = 0;
|
||||
int *spriteWidths = 0;
|
||||
QImage *variantGrids = 0;
|
||||
|
||||
void readStyleGenToken(const char *&from, const char *end, StyleGenTokenType &tokenType, string &token) {
|
||||
@@ -1353,17 +1354,22 @@ bool genStyles(const QString &classes_in, const QString &classes_out, const QStr
|
||||
}
|
||||
|
||||
QImage sprites[variantsCount];
|
||||
int widths[variantsCount] = { 0 };
|
||||
variantSprites = sprites;
|
||||
spriteWidths = widths;
|
||||
|
||||
QString sprite0(path_to_sprites + "sprite" + QString(variantPostfixes[0]) + ".png"), spriteLast(path_to_sprites + "sprite" + QString(variantPostfixes[variantsCount - 1]) + ".png");
|
||||
variantSprites[0] = QImage(sprite0);
|
||||
spriteWidths[0] = variantSprites[0].width();
|
||||
for (int i = 1; i < variantsCount - 1; ++i) {
|
||||
variantSprites[i] = QImage(adjustPx(variants[i], variantSprites[0].width(), true), adjustPx(variants[i], variantSprites[0].height(), true), QImage::Format_ARGB32_Premultiplied);
|
||||
spriteWidths[i] = variantSprites[i].width();
|
||||
QPainter p(&variantSprites[i]);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(0, 0, variantSprites[i].width(), variantSprites[i].height(), Qt::transparent);
|
||||
}
|
||||
variantSprites[variantsCount - 1] = QImage(spriteLast);
|
||||
spriteWidths[variantsCount - 1] = variantSprites[variantsCount - 1].width();
|
||||
|
||||
QPixmap spriteMaxPix = QPixmap::fromImage(variantSprites[variantsCount - 1], Qt::ColorOnly);
|
||||
spriteMax = &spriteMaxPix;
|
||||
@@ -1567,11 +1573,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
|
||||
tcpp << "\nnamespace style {\n\n";
|
||||
tcpp << "\tFontFamilies _fontFamilies;\n";
|
||||
tcpp << "\tFontDatas _fontsMap;\n";
|
||||
tcpp << "\tColorDatas _colorsMap;\n\n";
|
||||
tcpp << "\tColorDatas _colorsMap;\n";
|
||||
tcpp << "int _spriteWidth = " << spriteWidths[0] << ";\n\n";
|
||||
tcpp << "\tvoid startManager() {\n";
|
||||
|
||||
tcpp << "\n\t\tif (cRetina()) {\n";
|
||||
tcpp << "\t\t\tcSetRealScale(dbisOne);\n\n";
|
||||
tcpp << "\t\t\tcSetRealScale(dbisOne);\n";
|
||||
tcpp << "\t\t\t_spriteWidth = " << spriteWidths[variantsCount - 1] << ";\n\n";
|
||||
for (int i = 0, l = scalars.size(); i < l; ++i) {
|
||||
Scalar &sc(scalars[i]);
|
||||
if (sc.second.first == scSprite || sc.first == "spriteFile" || sc.first == "emojisFile" || sc.first == "emojiImgSize") {
|
||||
@@ -1594,6 +1602,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
|
||||
const char *varName = variantNames[i];
|
||||
|
||||
tcpp << "\t\tcase " << varName << ":\n";
|
||||
tcpp << "\t\t\t_spriteWidth = " << spriteWidths[i] << ";\n\n";
|
||||
|
||||
typedef QMap<string, int> FontFamilies;
|
||||
FontFamilies fontFamilies;
|
||||
|
||||
@@ -132,7 +132,7 @@ int main(int argc, const char * argv[]) {
|
||||
}
|
||||
|
||||
if (update) {
|
||||
writeLog(@"Starting update files iteration!");
|
||||
writeLog([@"Starting update files iteration, path: " stringByAppendingString: [workDir stringByAppendingString:@"tupdates/ready"]]);
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *srcDir = [workDir stringByAppendingString:@"tupdates/ready/"];
|
||||
@@ -142,6 +142,7 @@ int main(int argc, const char * argv[]) {
|
||||
includingPropertiesForKeys:keys
|
||||
options:0
|
||||
errorHandler:^(NSURL *url, NSError *error) {
|
||||
writeLog([[[@"Error in enumerating " stringByAppendingString:[url absoluteString]] stringByAppendingString: @" error is: "] stringByAppendingString: [error description]]);
|
||||
return NO;
|
||||
}];
|
||||
for (NSURL *url in enumerator) {
|
||||
|
||||
@@ -34,7 +34,6 @@ ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
void ApiWrap::init() {
|
||||
App::initMedia();
|
||||
}
|
||||
|
||||
void ApiWrap::itemRemoved(HistoryItem *item) {
|
||||
@@ -85,7 +84,7 @@ void ApiWrap::requestReplyTo(HistoryReply *reply, MsgId to) {
|
||||
}
|
||||
|
||||
void ApiWrap::requestFullPeer(PeerData *peer) {
|
||||
if (_fullRequests.contains(peer)) return;
|
||||
if (!peer || _fullRequests.contains(peer)) return;
|
||||
mtpRequestId req;
|
||||
if (peer->chat) {
|
||||
req = MTP::send(MTPmessages_GetFullChat(MTP_int(App::chatFromPeer(peer->id))), rpcDone(&ApiWrap::gotChatFull, peer), rpcFail(&ApiWrap::gotPeerFailed, peer));
|
||||
@@ -116,18 +115,21 @@ void ApiWrap::clearWebPageRequests() {
|
||||
|
||||
void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
|
||||
const MTPDmessages_chatFull &d(result.c_messages_chatFull());
|
||||
const MTPDchatFull &f(d.vfull_chat.c_chatFull());
|
||||
App::feedUsers(d.vusers);
|
||||
App::feedChats(d.vchats);
|
||||
App::feedParticipants(d.vfull_chat.c_chatFull().vparticipants);
|
||||
PhotoData *photo = App::feedPhoto(d.vfull_chat.c_chatFull().vchat_photo);
|
||||
if (photo) {
|
||||
ChatData *chat = peer->asChat();
|
||||
if (chat) {
|
||||
App::feedParticipants(f.vparticipants);
|
||||
PhotoData *photo = App::feedPhoto(f.vchat_photo);
|
||||
ChatData *chat = peer->asChat();
|
||||
if (chat) {
|
||||
if (photo) {
|
||||
chat->photoId = photo->id;
|
||||
photo->chat = chat;
|
||||
}
|
||||
chat->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString();
|
||||
}
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vfull_chat.c_chatFull().vnotify_settings);
|
||||
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
||||
|
||||
_fullRequests.remove(peer);
|
||||
emit fullPeerLoaded(peer);
|
||||
@@ -136,6 +138,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
|
||||
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
|
||||
const MTPDuserFull &d(result.c_userFull());
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
|
||||
App::feedPhoto(d.vprofile_photo);
|
||||
App::feedUserLink(MTP_int(App::userFromPeer(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace {
|
||||
typedef QHash<PeerId, PeerData*> PeersData;
|
||||
PeersData peersData;
|
||||
|
||||
typedef QMap<PeerData*, bool> MutedPeers;
|
||||
MutedPeers mutedPeers;
|
||||
|
||||
typedef QHash<PhotoId, PhotoData*> PhotosData;
|
||||
PhotosData photosData;
|
||||
|
||||
@@ -70,9 +73,12 @@ namespace {
|
||||
|
||||
HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0;
|
||||
|
||||
QPixmap *sprite = 0, *emojis = 0;
|
||||
QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0;
|
||||
|
||||
typedef QMap<uint32, QPixmap> EmojisMap;
|
||||
QPixmap *corners[RoundCornersCount][4] = { { 0 } };
|
||||
QImage *cornersMask[4] = { 0 };
|
||||
|
||||
typedef QMap<uint64, QPixmap> EmojisMap;
|
||||
EmojisMap mainEmojisMap;
|
||||
QMap<int32, EmojisMap> otherEmojisMap;
|
||||
|
||||
@@ -83,7 +89,8 @@ namespace {
|
||||
typedef QHash<PhotoData*, LastPhotosList::iterator> LastPhotosMap;
|
||||
LastPhotosMap lastPhotosMap;
|
||||
|
||||
style::color _msgServiceBG;
|
||||
style::color _msgServiceBg;
|
||||
style::color _msgServiceSelectBg;
|
||||
style::color _historyScrollBarColor;
|
||||
style::color _historyScrollBgColor;
|
||||
style::color _historyScrollBarOverColor;
|
||||
@@ -441,11 +448,12 @@ namespace App {
|
||||
return data;
|
||||
}
|
||||
|
||||
void feedChats(const MTPVector<MTPChat> &chats) {
|
||||
ChatData *feedChats(const MTPVector<MTPChat> &chats) {
|
||||
ChatData *data = 0;
|
||||
const QVector<MTPChat> &v(chats.c_vector().v);
|
||||
for (QVector<MTPChat>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
||||
const MTPchat &chat(*i);
|
||||
ChatData *data = 0;
|
||||
data = 0;
|
||||
QString title;
|
||||
switch (chat.type()) {
|
||||
case mtpc_chat: {
|
||||
@@ -458,7 +466,7 @@ namespace App {
|
||||
data->setPhoto(d.vphoto);
|
||||
data->date = d.vdate.v;
|
||||
data->count = d.vparticipants_count.v;
|
||||
data->left = false;
|
||||
data->left = d.vleft.v;
|
||||
data->forbidden = false;
|
||||
data->access = 0;
|
||||
if (data->version < d.vversion.v) {
|
||||
@@ -507,6 +515,7 @@ namespace App {
|
||||
|
||||
if (App::main()) App::main()->peerUpdated(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void feedParticipants(const MTPChatParticipants &p) {
|
||||
@@ -628,7 +637,7 @@ namespace App {
|
||||
const MTPDphotoSize &d(size.c_photoSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, d.vsize.v);
|
||||
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), d.vsize.v);
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
@@ -637,17 +646,43 @@ namespace App {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, bytes);
|
||||
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), bytes);
|
||||
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return ImagePtr(d.vw.v, d.vh.v, 0, 0, 0, 0, bytes);
|
||||
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0), bytes);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return ImagePtr();
|
||||
}
|
||||
|
||||
StorageImageLocation imageLocation(const MTPPhotoSize &size) {
|
||||
switch (size.type()) {
|
||||
case mtpc_photoSize: {
|
||||
const MTPDphotoSize &d(size.c_photoSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
const MTPDphotoCachedSize &d(size.c_photoCachedSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
|
||||
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return StorageImageLocation();
|
||||
}
|
||||
|
||||
void feedWereRead(const QVector<MTPint> &msgsIds) {
|
||||
for (QVector<MTPint>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
|
||||
MsgsData::const_iterator j = msgsData.constFind(i->v);
|
||||
@@ -861,6 +896,18 @@ namespace App {
|
||||
return App::video(video.vid.v, convert, video.vaccess_hash.v, video.vuser_id.v, video.vdate.v, video.vduration.v, video.vw.v, video.vh.v, App::image(video.vthumb), video.vdc_id.v, video.vsize.v);
|
||||
}
|
||||
|
||||
AudioData *feedAudio(const MTPaudio &audio, AudioData *convert) {
|
||||
switch (audio.type()) {
|
||||
case mtpc_audio: {
|
||||
return feedAudio(audio.c_audio(), convert);
|
||||
} break;
|
||||
case mtpc_audioEmpty: {
|
||||
return App::audio(audio.c_audioEmpty().vid.v, convert);
|
||||
} break;
|
||||
}
|
||||
return App::audio(0);
|
||||
}
|
||||
|
||||
AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert) {
|
||||
return App::audio(audio.vid.v, convert, audio.vaccess_hash.v, audio.vuser_id.v, audio.vdate.v, qs(audio.vmime_type), audio.vduration.v, audio.vdc_id.v, audio.vsize.v);
|
||||
}
|
||||
@@ -869,7 +916,7 @@ namespace App {
|
||||
switch (document.type()) {
|
||||
case mtpc_document: {
|
||||
const MTPDdocument &d(document.c_document());
|
||||
return App::document(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v);
|
||||
return App::documentSet(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v, StorageImageLocation());
|
||||
} break;
|
||||
case mtpc_documentEmpty: return App::document(document.c_documentEmpty().vid.v);
|
||||
}
|
||||
@@ -882,14 +929,14 @@ namespace App {
|
||||
return feedDocument(document.c_document(), convert);
|
||||
} break;
|
||||
case mtpc_documentEmpty: {
|
||||
return App::document(document.c_documentEmpty().vid.v, convert);
|
||||
return App::documentSet(document.c_documentEmpty().vid.v, convert, 0, 0, QVector<MTPDocumentAttribute>(), QString(), ImagePtr(), 0, 0, StorageImageLocation());
|
||||
} break;
|
||||
}
|
||||
return App::document(0);
|
||||
}
|
||||
|
||||
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert) {
|
||||
return App::document(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v);
|
||||
return App::documentSet(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v, App::imageLocation(document.vthumb));
|
||||
}
|
||||
|
||||
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
|
||||
@@ -903,7 +950,11 @@ namespace App {
|
||||
WebPageData *feedWebPage(const MTPWebPage &webpage) {
|
||||
switch (webpage.type()) {
|
||||
case mtpc_webPage: return App::feedWebPage(webpage.c_webPage());
|
||||
case mtpc_webPageEmpty: return App::webPage(webpage.c_webPageEmpty().vid.v);
|
||||
case mtpc_webPageEmpty: {
|
||||
WebPageData *page = App::webPage(webpage.c_webPageEmpty().vid.v);
|
||||
if (page->pendingTill > 0) page->pendingTill = -1; // failed
|
||||
return page;
|
||||
} break;
|
||||
case mtpc_webPagePending: return App::feedWebPage(webpage.c_webPagePending());
|
||||
}
|
||||
return 0;
|
||||
@@ -1123,7 +1174,15 @@ namespace App {
|
||||
return result;
|
||||
}
|
||||
|
||||
DocumentData *document(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) {
|
||||
DocumentData *document(const DocumentId &document) {
|
||||
DocumentsData::const_iterator i = documentsData.constFind(document);
|
||||
if (i == documentsData.cend()) {
|
||||
i = documentsData.insert(document, new DocumentData(document));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation) {
|
||||
if (convert) {
|
||||
if (convert->id != document) {
|
||||
DocumentsData::iterator i = documentsData.find(convert->id);
|
||||
@@ -1141,12 +1200,28 @@ namespace App {
|
||||
convert->thumb = thumb;
|
||||
convert->dc = dc;
|
||||
convert->size = size;
|
||||
} else if (convert->thumb->isNull() && !thumb->isNull()) {
|
||||
convert->thumb = thumb;
|
||||
} else {
|
||||
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) {
|
||||
convert->thumb = thumb;
|
||||
}
|
||||
if (convert->sticker && !attributes.isEmpty() && (convert->sticker->alt.isEmpty() || convert->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
|
||||
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
|
||||
if (i->type() == mtpc_documentAttributeSticker) {
|
||||
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
|
||||
if (d.valt.c_string().v.length() > 0) {
|
||||
convert->sticker->alt = qs(d.valt);
|
||||
convert->sticker->set = d.vstickerset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (convert->sticker && !convert->sticker->loc.dc && thumbLocation.dc) {
|
||||
convert->sticker->loc = thumbLocation;
|
||||
}
|
||||
|
||||
if (convert->location.check()) {
|
||||
Local::writeFileLocation(mediaKey(mtpc_inputDocumentFileLocation, convert->dc, convert->id), convert->location);
|
||||
Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), convert->location);
|
||||
}
|
||||
}
|
||||
DocumentsData::const_iterator i = documentsData.constFind(document);
|
||||
@@ -1156,6 +1231,7 @@ namespace App {
|
||||
result = convert;
|
||||
} else {
|
||||
result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
|
||||
if (result->sticker) result->sticker->loc = thumbLocation;
|
||||
}
|
||||
documentsData.insert(document, result);
|
||||
} else {
|
||||
@@ -1170,19 +1246,23 @@ namespace App {
|
||||
result->dc = dc;
|
||||
result->size = size;
|
||||
} else {
|
||||
if (result->thumb->isNull() && !thumb->isNull()) {
|
||||
if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
|
||||
result->thumb = thumb;
|
||||
}
|
||||
if (result->alt.isEmpty()) {
|
||||
if (result->sticker && !attributes.isEmpty() && (result->sticker->alt.isEmpty() || result->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
|
||||
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
|
||||
if (i->type() == mtpc_documentAttributeSticker) {
|
||||
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
|
||||
if (d.valt.c_string().v.length() > 0) {
|
||||
result->alt = qs(d.valt);
|
||||
result->sticker->alt = qs(d.valt);
|
||||
result->sticker->set = d.vstickerset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result->sticker && !result->sticker->loc.dc && thumbLocation.dc) {
|
||||
result->sticker->loc = thumbLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1288,7 +1368,7 @@ namespace App {
|
||||
photoSizes.push_back(MTP_photoSize(MTP_string("a"), uphoto.vphoto_small, MTP_int(160), MTP_int(160), MTP_int(0)));
|
||||
photoSizes.push_back(MTP_photoSize(MTP_string("c"), uphoto.vphoto_big, MTP_int(640), MTP_int(640), MTP_int(0)));
|
||||
|
||||
return MTP_photo(uphoto.vphoto_id, MTP_long(0), userId, date, MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
return MTP_photo(uphoto.vphoto_id, MTP_long(0), userId, date, MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
}
|
||||
return MTP_photoEmpty(MTP_long(0));
|
||||
}
|
||||
@@ -1431,6 +1511,7 @@ namespace App {
|
||||
void historyClearItems() {
|
||||
historyClearMsgs();
|
||||
randomData.clear();
|
||||
mutedPeers.clear();
|
||||
for (PeersData::const_iterator i = peersData.cbegin(), e = peersData.cend(); i != e; ++i) {
|
||||
delete *i;
|
||||
}
|
||||
@@ -1458,8 +1539,11 @@ namespace App {
|
||||
if (api()) api()->clearWebPageRequests();
|
||||
cSetRecentStickers(RecentStickerPack());
|
||||
cSetStickersHash(QByteArray());
|
||||
cSetStickers(AllStickers());
|
||||
cSetEmojiStickers(EmojiStickersMap());
|
||||
cSetStickerSets(StickerSets());
|
||||
cSetStickerSetsOrder(StickerSetsOrder());
|
||||
cSetLastStickersUpdate(0);
|
||||
LOG(("Stickers: cleared everything!"));
|
||||
::videoItems.clear();
|
||||
::audioItems.clear();
|
||||
::documentItems.clear();
|
||||
@@ -1510,19 +1594,81 @@ namespace App {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prepareCorners(RoundCorners index, int32 radius, const style::color &color, const style::color *shadow = 0, QImage *cors = 0) {
|
||||
int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor();
|
||||
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4];
|
||||
{
|
||||
QPainter p(&rect);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(QRect(0, 0, rect.width(), rect.height()), st::transparent->b);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
p.setPen(Qt::NoPen);
|
||||
if (shadow) {
|
||||
p.setBrush((*shadow)->b);
|
||||
p.drawRoundedRect(0, s, r * 3, r * 3, r, r);
|
||||
}
|
||||
p.setBrush(color->b);
|
||||
p.drawRoundedRect(0, 0, r * 3, r * 3, r, r);
|
||||
}
|
||||
if (!cors) cors = localCors;
|
||||
cors[0] = rect.copy(0, 0, r, r);
|
||||
cors[1] = rect.copy(r * 2, 0, r, r);
|
||||
cors[2] = rect.copy(0, r * 2, r, r + (shadow ? s : 0));
|
||||
cors[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0));
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
::corners[index][i] = new QPixmap(QPixmap::fromImage(cors[i], Qt::ColorOnly));
|
||||
::corners[index][i]->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
}
|
||||
|
||||
void initMedia() {
|
||||
deinitMedia(false);
|
||||
audioInit();
|
||||
|
||||
if (!::sprite) {
|
||||
::sprite = new QPixmap(st::spriteFile);
|
||||
if (rtl()) {
|
||||
::sprite = new QPixmap(QPixmap::fromImage(QImage(st::spriteFile).mirrored(true, false)));
|
||||
} else {
|
||||
::sprite = new QPixmap(st::spriteFile);
|
||||
}
|
||||
if (cRetina()) ::sprite->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
emojiInit();
|
||||
if (!::emojis) {
|
||||
::emojis = new QPixmap(st::emojisFile);
|
||||
::emojis = new QPixmap(QLatin1String(EName));
|
||||
if (cRetina()) ::emojis->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
initEmoji();
|
||||
if (!::emojisLarge) {
|
||||
::emojisLarge = new QPixmap(QLatin1String(EmojiNames[EIndex + 1]));
|
||||
if (cRetina()) ::emojisLarge->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
QImage mask[4];
|
||||
prepareCorners(MaskCorners, st::msgRadius, st::white, 0, mask);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
::cornersMask[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied));
|
||||
::cornersMask[i]->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
prepareCorners(BlackCorners, st::msgRadius, st::black);
|
||||
prepareCorners(ServiceCorners, st::msgRadius, st::msgServiceBg);
|
||||
prepareCorners(ServiceSelectedCorners, st::msgRadius, st::msgServiceSelectBg);
|
||||
prepareCorners(SelectedOverlayCorners, st::msgRadius, st::msgSelectOverlay);
|
||||
prepareCorners(DateCorners, st::msgRadius, st::msgDateImgBg);
|
||||
prepareCorners(DateSelectedCorners, st::msgRadius, st::msgDateImgSelectBg);
|
||||
prepareCorners(InShadowCorners, st::msgRadius, st::msgInShadow);
|
||||
prepareCorners(InSelectedShadowCorners, st::msgRadius, st::msgInSelectShadow);
|
||||
prepareCorners(ForwardCorners, st::msgRadius, st::forwardBg);
|
||||
prepareCorners(MediaviewSaveCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(EmojiHoverCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(StickerHoverCorners, st::msgRadius, st::emojiPanHover);
|
||||
|
||||
prepareCorners(MessageInCorners, st::msgRadius, st::msgInBg, &st::msgInShadow);
|
||||
prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInSelectBg, &st::msgInSelectShadow);
|
||||
prepareCorners(MessageOutCorners, st::msgRadius, st::msgOutBg, &st::msgOutShadow);
|
||||
prepareCorners(MessageOutSelectedCorners, st::msgRadius, st::msgOutSelectBg, &st::msgOutSelectShadow);
|
||||
prepareCorners(ButtonHoverCorners, st::msgRadius, st::mediaSaveButton.overBgColor, &st::msgInShadow);
|
||||
|
||||
}
|
||||
|
||||
void deinitMedia(bool completely) {
|
||||
@@ -1538,6 +1684,14 @@ namespace App {
|
||||
::sprite = 0;
|
||||
delete ::emojis;
|
||||
::emojis = 0;
|
||||
delete ::emojisLarge;
|
||||
::emojisLarge = 0;
|
||||
for (int32 j = 0; j < 4; ++j) {
|
||||
for (int32 i = 0; i < RoundCornersCount; ++i) {
|
||||
delete ::corners[i][j]; ::corners[i][j] = 0;
|
||||
}
|
||||
delete ::cornersMask[j]; ::cornersMask[j] = 0;
|
||||
}
|
||||
mainEmojisMap.clear();
|
||||
otherEmojisMap.clear();
|
||||
|
||||
@@ -1598,27 +1752,33 @@ namespace App {
|
||||
return ::mousedItem;
|
||||
}
|
||||
|
||||
QPixmap &sprite() {
|
||||
const QPixmap &sprite() {
|
||||
return *::sprite;
|
||||
}
|
||||
|
||||
QPixmap &emojis() {
|
||||
const QPixmap &emojis() {
|
||||
return *::emojis;
|
||||
}
|
||||
|
||||
const QPixmap &emojiSingle(const EmojiData *emoji, int32 fontHeight) {
|
||||
const QPixmap &emojisLarge() {
|
||||
return *::emojisLarge;
|
||||
}
|
||||
|
||||
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight) {
|
||||
EmojisMap *map = &(fontHeight == st::taDefFlat.font->height ? mainEmojisMap : otherEmojisMap[fontHeight]);
|
||||
EmojisMap::const_iterator i = map->constFind(emoji->code);
|
||||
EmojisMap::const_iterator i = map->constFind(emojiKey(emoji));
|
||||
if (i == map->cend()) {
|
||||
QImage img(st::emojiImgSize + st::emojiPadding * cIntRetinaFactor() * 2, fontHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
QImage img(ESize + st::emojiPadding * cIntRetinaFactor() * 2, fontHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
if (cRetina()) img.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&img);
|
||||
QPainter::CompositionMode m = p.compositionMode();
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(0, 0, img.width(), img.height(), Qt::transparent);
|
||||
p.drawPixmap(QPoint(st::emojiPadding * cIntRetinaFactor(), (fontHeight * cIntRetinaFactor() - st::emojiImgSize) / 2), App::emojis(), QRect(emoji->x, emoji->y, st::emojiImgSize, st::emojiImgSize));
|
||||
p.setCompositionMode(m);
|
||||
emojiDraw(p, emoji, st::emojiPadding * cIntRetinaFactor(), (fontHeight * cIntRetinaFactor() - ESize) / 2);
|
||||
}
|
||||
i = map->insert(emoji->code, QPixmap::fromImage(img, Qt::ColorOnly));
|
||||
i = map->insert(emojiKey(emoji), QPixmap::fromImage(img, Qt::ColorOnly));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
@@ -1768,6 +1928,34 @@ namespace App {
|
||||
return ::webPageItems;
|
||||
}
|
||||
|
||||
void regMuted(PeerData *peer, int32 changeIn) {
|
||||
::mutedPeers.insert(peer, true);
|
||||
if (App::main()) App::main()->updateMutedIn(changeIn);
|
||||
}
|
||||
|
||||
void unregMuted(PeerData *peer) {
|
||||
::mutedPeers.remove(peer);
|
||||
}
|
||||
|
||||
void updateMuted() {
|
||||
int32 changeInMin = 0;
|
||||
for (MutedPeers::iterator i = ::mutedPeers.begin(); i != ::mutedPeers.end();) {
|
||||
int32 changeIn = 0;
|
||||
History *h = App::history(i.key()->id);
|
||||
if (isNotifyMuted(i.key()->notify, &changeIn)) {
|
||||
h->setMute(true);
|
||||
if (changeIn && (!changeInMin || changeIn < changeInMin)) {
|
||||
changeInMin = changeIn;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
h->setMute(false);
|
||||
i = ::mutedPeers.erase(i);
|
||||
}
|
||||
}
|
||||
if (changeInMin) App::main()->updateMutedIn(changeInMin);
|
||||
}
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager) {
|
||||
if (cConnectionType() == dbictHttpProxy) {
|
||||
const ConnectionProxy &p(cConnectionProxy());
|
||||
@@ -1798,26 +1986,86 @@ namespace App {
|
||||
}
|
||||
}
|
||||
|
||||
void joinGroupByHash(const QString &hash) {
|
||||
if (App::main()) {
|
||||
App::main()->joinGroupByHash(hash);
|
||||
}
|
||||
}
|
||||
|
||||
void stickersBox(const QString &name) {
|
||||
if (App::main()) {
|
||||
App::main()->stickersBox(MTP_inputStickerSetShortName(MTP_string(name)));
|
||||
}
|
||||
}
|
||||
|
||||
void openLocalUrl(const QString &url) {
|
||||
if (App::main()) {
|
||||
App::main()->openLocalUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
QImage **cornersMask() {
|
||||
return ::cornersMask;
|
||||
}
|
||||
QPixmap **corners(RoundCorners index) {
|
||||
return ::corners[index];
|
||||
}
|
||||
|
||||
void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) {
|
||||
QPixmap **c = ::corners[index];
|
||||
int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor();
|
||||
if (w < 2 * cw || h < 2 * ch) return;
|
||||
if (w > 2 * cw) {
|
||||
p.fillRect(QRect(x + cw, y, w - 2 * cw, ch), bg->b);
|
||||
p.fillRect(QRect(x + cw, y + h - ch, w - 2 * cw, ch), bg->b);
|
||||
if (sh) p.fillRect(QRect(x + cw, y + h, w - 2 * cw, st::msgShadow), (*sh)->b);
|
||||
}
|
||||
if (h > 2 * ch) {
|
||||
p.fillRect(QRect(x, y + ch, w, h - 2 * ch), bg->b);
|
||||
}
|
||||
p.drawPixmap(QPoint(x, y), *c[0]);
|
||||
p.drawPixmap(QPoint(x + w - cw, y), *c[1]);
|
||||
p.drawPixmap(QPoint(x, y + h - ch), *c[2]);
|
||||
p.drawPixmap(QPoint(x + w - cw, y + h - ch), *c[3]);
|
||||
}
|
||||
|
||||
void roundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index) {
|
||||
QPixmap **c = App::corners(index);
|
||||
int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor();
|
||||
p.fillRect(x + cw, y + h, w - 2 * cw, st::msgShadow, sh->b);
|
||||
p.fillRect(x, y + h - ch, cw, st::msgShadow, sh->b);
|
||||
p.fillRect(x + w - cw, y + h - ch, cw, st::msgShadow, sh->b);
|
||||
p.drawPixmap(x, y + h - ch + st::msgShadow, *c[2]);
|
||||
p.drawPixmap(x + w - cw, y + h - ch + st::msgShadow, *c[3]);
|
||||
}
|
||||
|
||||
void initBackground(int32 id, const QImage &p, bool nowrite) {
|
||||
if (Local::readBackground()) return;
|
||||
|
||||
QImage img(p);
|
||||
bool remove = false;
|
||||
if (p.isNull()) {
|
||||
img.load(st::msgBG);
|
||||
id = 0;
|
||||
if (id == DefaultChatBackground) {
|
||||
img.load(st::msgBG);
|
||||
} else {
|
||||
img.load(st::msgBG0);
|
||||
if (cRetina()) {
|
||||
img = img.scaledToWidth(img.width() * 2, Qt::SmoothTransformation);
|
||||
} else if (cScale() != dbisOne) {
|
||||
img = img.scaledToWidth(convertScale(img.width()), Qt::SmoothTransformation);
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
remove = true;
|
||||
}
|
||||
if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_ARGB32_Premultiplied && img.format() != QImage::Format_RGB32) {
|
||||
img = img.convertToFormat(QImage::Format_RGB32);
|
||||
}
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
|
||||
if (!nowrite) Local::writeBackground(id, img);
|
||||
if (!nowrite) {
|
||||
Local::writeBackground(id, remove ? QImage() : img);
|
||||
}
|
||||
|
||||
delete cChatBackground();
|
||||
cSetChatBackground(new QPixmap(QPixmap::fromImage(img, Qt::ColorOnly)));
|
||||
@@ -1933,7 +2181,17 @@ namespace App {
|
||||
components[maxtomin[0]] = max;
|
||||
|
||||
uchar r = uchar(components[0]), g = uchar(components[1]), b = uchar(components[2]);
|
||||
_msgServiceBG = style::color(r, g, b, qRound(st::msgServiceBG->c.alphaF() * 0xFF));
|
||||
float64 alpha = st::msgServiceBg->c.alphaF();
|
||||
_msgServiceBg = style::color(r, g, b, qRound(alpha * 0xFF));
|
||||
|
||||
float64 alphaSel = st::msgServiceSelectBg->c.alphaF(), addSel = (1. - ((1. - alphaSel) / (1. - alpha))) * 0xFF;
|
||||
uchar rsel = snap(qRound(((1. - alphaSel) * r + addSel) / alphaSel), 0, 0xFF);
|
||||
uchar gsel = snap(qRound(((1. - alphaSel) * g + addSel) / alphaSel), 0, 0xFF);
|
||||
uchar bsel = snap(qRound(((1. - alphaSel) * b + addSel) / alphaSel), 0, 0xFF);
|
||||
_msgServiceSelectBg = style::color(r, g, b, qRound(alphaSel * 0xFF));
|
||||
|
||||
prepareCorners(ServiceCorners, st::msgRadius, _msgServiceBg);
|
||||
prepareCorners(ServiceSelectedCorners, st::msgRadius, _msgServiceSelectBg);
|
||||
|
||||
uchar rScroll = uchar(componentsScroll[0]), gScroll = uchar(componentsScroll[1]), bScroll = uchar(componentsScroll[2]);
|
||||
_historyScrollBarColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barColor->c.alphaF() * 0xFF));
|
||||
@@ -1946,27 +2204,31 @@ namespace App {
|
||||
if (App::main()) App::main()->updateScrollColors();
|
||||
}
|
||||
|
||||
style::color msgServiceBG() {
|
||||
return _msgServiceBG;
|
||||
const style::color &msgServiceBg() {
|
||||
return _msgServiceBg;
|
||||
}
|
||||
|
||||
style::color historyScrollBarColor() {
|
||||
const style::color &msgServiceSelectBg() {
|
||||
return _msgServiceSelectBg;
|
||||
}
|
||||
|
||||
const style::color &historyScrollBarColor() {
|
||||
return _historyScrollBarColor;
|
||||
}
|
||||
|
||||
style::color historyScrollBgColor() {
|
||||
const style::color &historyScrollBgColor() {
|
||||
return _historyScrollBgColor;
|
||||
}
|
||||
|
||||
style::color historyScrollBarOverColor() {
|
||||
const style::color &historyScrollBarOverColor() {
|
||||
return _historyScrollBarOverColor;
|
||||
}
|
||||
|
||||
style::color historyScrollBgOverColor() {
|
||||
const style::color &historyScrollBgOverColor() {
|
||||
return _historyScrollBgOverColor;
|
||||
}
|
||||
|
||||
style::color introPointHoverColor() {
|
||||
const style::color &introPointHoverColor() {
|
||||
return _introPointHoverColor;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,31 @@ typedef QHash<AudioData*, HistoryItemsMap> AudioItems;
|
||||
typedef QHash<DocumentData*, HistoryItemsMap> DocumentItems;
|
||||
typedef QHash<WebPageData*, HistoryItemsMap> WebPageItems;
|
||||
|
||||
enum RoundCorners {
|
||||
MaskCorners = 0x00, // for images
|
||||
BlackCorners,
|
||||
ServiceCorners,
|
||||
ServiceSelectedCorners,
|
||||
SelectedOverlayCorners,
|
||||
DateCorners,
|
||||
DateSelectedCorners,
|
||||
ForwardCorners,
|
||||
MediaviewSaveCorners,
|
||||
EmojiHoverCorners,
|
||||
StickerHoverCorners,
|
||||
|
||||
InShadowCorners, // for photos without bg
|
||||
InSelectedShadowCorners,
|
||||
|
||||
MessageInCorners, // with shadow
|
||||
MessageInSelectedCorners,
|
||||
MessageOutCorners,
|
||||
MessageOutSelectedCorners,
|
||||
ButtonHoverCorners,
|
||||
|
||||
RoundCornersCount
|
||||
};
|
||||
|
||||
namespace App {
|
||||
Application *app();
|
||||
Window *wnd();
|
||||
@@ -72,8 +97,8 @@ namespace App {
|
||||
QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
|
||||
bool onlineColorUse(int32 online, int32 now);
|
||||
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users); // returnes last user
|
||||
void feedChats(const MTPVector<MTPChat> &chats);
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
|
||||
ChatData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
|
||||
void feedParticipants(const MTPChatParticipants &p);
|
||||
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
|
||||
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
|
||||
@@ -87,11 +112,13 @@ namespace App {
|
||||
int32 maxMsgId();
|
||||
|
||||
ImagePtr image(const MTPPhotoSize &size);
|
||||
StorageImageLocation imageLocation(const MTPPhotoSize &size);
|
||||
|
||||
PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs);
|
||||
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert = 0);
|
||||
PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert = 0);
|
||||
VideoData *feedVideo(const MTPDvideo &video, VideoData *convert = 0);
|
||||
AudioData *feedAudio(const MTPaudio &audio, AudioData *convert = 0);
|
||||
AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert = 0);
|
||||
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
|
||||
DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = 0);
|
||||
@@ -117,7 +144,8 @@ namespace App {
|
||||
PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr());
|
||||
VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
|
||||
AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
|
||||
DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
|
||||
DocumentData *document(const DocumentId &document);
|
||||
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation);
|
||||
WebPageData *webPage(const WebPageId &webPage, WebPageData *convert = 0, const QString &type = QString(), const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = -2);
|
||||
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
|
||||
void forgetMedia();
|
||||
@@ -154,9 +182,10 @@ namespace App {
|
||||
void mousedItem(HistoryItem *item);
|
||||
HistoryItem *mousedItem();
|
||||
|
||||
QPixmap &sprite();
|
||||
QPixmap &emojis();
|
||||
const QPixmap &emojiSingle(const EmojiData *emoji, int32 fontHeight);
|
||||
const QPixmap &sprite();
|
||||
const QPixmap &emojis();
|
||||
const QPixmap &emojisLarge();
|
||||
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight);
|
||||
|
||||
void initMedia();
|
||||
void deinitMedia(bool completely = true);
|
||||
@@ -189,21 +218,39 @@ namespace App {
|
||||
void unregWebPageItem(WebPageData *data, HistoryItem *item);
|
||||
const WebPageItems &webPageItems();
|
||||
|
||||
void regMuted(PeerData *peer, int32 changeIn);
|
||||
void unregMuted(PeerData *peer);
|
||||
void updateMuted();
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager);
|
||||
void setProxySettings(QTcpSocket &socket);
|
||||
|
||||
void searchByHashtag(const QString &tag);
|
||||
void openUserByName(const QString &username, bool toProfile = false);
|
||||
void joinGroupByHash(const QString &hash);
|
||||
void stickersBox(const QString &name);
|
||||
void openLocalUrl(const QString &url);
|
||||
|
||||
void initBackground(int32 id = 0, const QImage &p = QImage(), bool nowrite = false);
|
||||
QImage **cornersMask();
|
||||
QPixmap **corners(RoundCorners index);
|
||||
void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0);
|
||||
inline void roundRect(QPainter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) {
|
||||
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, sh);
|
||||
}
|
||||
void roundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index);
|
||||
inline void roundShadow(QPainter &p, const QRect &rect, const style::color &sh, RoundCorners index) {
|
||||
return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), sh, index);
|
||||
}
|
||||
|
||||
style::color msgServiceBG();
|
||||
style::color historyScrollBarColor();
|
||||
style::color historyScrollBgColor();
|
||||
style::color historyScrollBarOverColor();
|
||||
style::color historyScrollBgOverColor();
|
||||
style::color introPointHoverColor();
|
||||
void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false);
|
||||
|
||||
const style::color &msgServiceBg();
|
||||
const style::color &msgServiceSelectBg();
|
||||
const style::color &historyScrollBarColor();
|
||||
const style::color &historyScrollBgColor();
|
||||
const style::color &historyScrollBarOverColor();
|
||||
const style::color &historyScrollBgOverColor();
|
||||
const style::color &introPointHoverColor();
|
||||
|
||||
struct WallPaper {
|
||||
WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) {
|
||||
|
||||
@@ -59,7 +59,15 @@ namespace {
|
||||
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
|
||||
App::wnd()->minimizeToTray();
|
||||
return true;
|
||||
} else {
|
||||
App::wnd()->hide();
|
||||
App::wnd()->updateIsActive(cOfflineBlurTimeout());
|
||||
App::wnd()->updateGlobalMenu();
|
||||
return true;
|
||||
}
|
||||
} else if (ev->key() == Qt::Key_M && (ev->modifiers() & (Qt::MetaModifier | Qt::ControlModifier))) {
|
||||
App::wnd()->setWindowState(Qt::WindowMinimized);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(o, e);
|
||||
@@ -473,13 +481,13 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
|
||||
|
||||
PhotoId id = MTP::nonce<PhotoId>();
|
||||
|
||||
MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(MTP::authedId()), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes)));
|
||||
MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(MTP::authedId()), MTP_int(unixtime()), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes)));
|
||||
|
||||
QString file, filename;
|
||||
int32 filesize = 0;
|
||||
QByteArray data;
|
||||
|
||||
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
|
||||
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, MTP_audioEmpty(MTP_long(0)), photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
|
||||
|
||||
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
|
||||
|
||||
@@ -654,9 +662,11 @@ void Application::checkMapVersion() {
|
||||
psRegisterCustomScheme();
|
||||
if (Local::oldMapVersion()) {
|
||||
QString versionFeatures;
|
||||
if (DevChannel && Local::oldMapVersion() < 8002) {
|
||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Link previews bugfixes\n\xe2\x80\x94 Links in preview descriptions are now clickable\n\xe2\x80\x94 Twitter and Instagram mentions and hashtags in previews are clickable\n\xe2\x80\x94 Fixed file uploading\n\xe2\x80\x94 Fixed photo, document and sticker forwarding").replace('@', qsl("@") + QChar(0x200D));
|
||||
} else if (!DevChannel && Local::oldMapVersion() < 8004) {
|
||||
if (DevChannel && Local::oldMapVersion() < 8015) {
|
||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Video captions are displayed\n\xe2\x80\x94 Photo captions are displayed in photo viewer\n\xe2\x80\x94 Round corners for messages").replace('@', qsl("@") + QChar(0x200D));
|
||||
} else if (!DevChannel && Local::oldMapVersion() < 8016) {
|
||||
versionFeatures = lang(lng_new_version_text).trimmed();
|
||||
} else if (!DevChannel && Local::oldMapVersion() < 8017) {
|
||||
versionFeatures = lang(lng_new_version_minor).trimmed();
|
||||
}
|
||||
if (!versionFeatures.isEmpty()) {
|
||||
@@ -672,6 +682,8 @@ void Application::startApp() {
|
||||
|
||||
DEBUG_LOG(("Application Info: starting app.."));
|
||||
|
||||
QMimeDatabase().mimeTypeForName(qsl("text/plain")); // create mime database
|
||||
|
||||
window->createWinId();
|
||||
window->init();
|
||||
|
||||
|
||||
BIN
Telegram/SourceFiles/art/bg.jpg
Normal file
|
After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 526 KiB |
|
Before Width: | Height: | Size: 528 KiB |
BIN
Telegram/SourceFiles/art/emoji.webp
Normal file
|
After Width: | Height: | Size: 586 KiB |
|
Before Width: | Height: | Size: 722 KiB |
BIN
Telegram/SourceFiles/art/emoji_125x.webp
Normal file
|
After Width: | Height: | Size: 789 KiB |
|
Before Width: | Height: | Size: 1003 KiB |
BIN
Telegram/SourceFiles/art/emoji_150x.webp
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
BIN
Telegram/SourceFiles/art/emoji_200x.webp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
Telegram/SourceFiles/art/emoji_250x.webp
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 722 KiB |
|
Before Width: | Height: | Size: 531 KiB |
|
Before Width: | Height: | Size: 956 KiB |
|
Before Width: | Height: | Size: 402 KiB |
|
Before Width: | Height: | Size: 532 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 217 KiB |
@@ -24,33 +24,35 @@ bool audioWorks();
|
||||
void audioPlayNotify();
|
||||
void audioFinish();
|
||||
|
||||
enum VoiceMessageState {
|
||||
VoiceMessageStopped,
|
||||
VoiceMessageStarting,
|
||||
VoiceMessagePlaying,
|
||||
VoiceMessageFinishing,
|
||||
VoiceMessagePausing,
|
||||
VoiceMessagePaused,
|
||||
VoiceMessageResuming,
|
||||
enum AudioPlayerState {
|
||||
AudioPlayerStopped,
|
||||
AudioPlayerStoppedAtStart,
|
||||
AudioPlayerStarting,
|
||||
AudioPlayerPlaying,
|
||||
AudioPlayerFinishing,
|
||||
AudioPlayerPausing,
|
||||
AudioPlayerPaused,
|
||||
AudioPlayerResuming,
|
||||
};
|
||||
|
||||
class VoiceMessagesFader;
|
||||
class VoiceMessagesLoader;
|
||||
class AudioPlayerFader;
|
||||
class AudioPlayerLoaders;
|
||||
|
||||
class VoiceMessages : public QObject {
|
||||
class AudioPlayer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoiceMessages();
|
||||
AudioPlayer();
|
||||
|
||||
void play(AudioData *audio);
|
||||
void pauseresume();
|
||||
|
||||
void currentState(AudioData **audio, VoiceMessageState *state = 0, int64 *position = 0, int64 *duration = 0);
|
||||
void currentState(AudioData **audio, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
|
||||
void clearStoppedAtStart(AudioData *audio);
|
||||
void processContext();
|
||||
|
||||
~VoiceMessages();
|
||||
~AudioPlayer();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -70,8 +72,8 @@ private:
|
||||
bool updateCurrentStarted(int32 pos = -1);
|
||||
|
||||
struct Msg {
|
||||
Msg() : audio(0), position(0), duration(0), skipStart(0), skipEnd(0), loading(0), started(0),
|
||||
state(VoiceMessageStopped), source(0), nextBuffer(0) {
|
||||
Msg() : audio(0), position(0), duration(0), frequency(AudioVoiceMsgFrequency), skipStart(0), skipEnd(0), loading(0), started(0),
|
||||
state(AudioPlayerStopped), source(0), nextBuffer(0) {
|
||||
memset(buffers, 0, sizeof(buffers));
|
||||
memset(samplesCount, 0, sizeof(samplesCount));
|
||||
}
|
||||
@@ -79,10 +81,11 @@ private:
|
||||
QString fname;
|
||||
QByteArray data;
|
||||
int64 position, duration;
|
||||
int32 frequency;
|
||||
int64 skipStart, skipEnd;
|
||||
bool loading;
|
||||
int64 started;
|
||||
VoiceMessageState state;
|
||||
AudioPlayerState state;
|
||||
|
||||
uint32 source;
|
||||
int32 nextBuffer;
|
||||
@@ -95,24 +98,58 @@ private:
|
||||
|
||||
QMutex _mutex;
|
||||
|
||||
friend class VoiceMessagesFader;
|
||||
friend class VoiceMessagesLoader;
|
||||
friend class AudioPlayerFader;
|
||||
friend class AudioPlayerLoaders;
|
||||
|
||||
QThread _faderThread;
|
||||
QThread _loaderThread;
|
||||
VoiceMessagesFader *_fader;
|
||||
VoiceMessagesLoader *_loader;
|
||||
QThread _faderThread, _loaderThread;
|
||||
AudioPlayerFader *_fader;
|
||||
AudioPlayerLoaders *_loader;
|
||||
|
||||
};
|
||||
|
||||
VoiceMessages *audioVoice();
|
||||
class AudioCaptureInner;
|
||||
|
||||
class VoiceMessagesFader : public QObject {
|
||||
class AudioCapture : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoiceMessagesFader(QThread *thread);
|
||||
AudioCapture();
|
||||
|
||||
void start();
|
||||
void stop(bool needResult);
|
||||
|
||||
bool check();
|
||||
|
||||
~AudioCapture();
|
||||
|
||||
signals:
|
||||
|
||||
void captureOnStart();
|
||||
void captureOnStop(bool needResult);
|
||||
|
||||
void onDone(QByteArray data, qint32 samples);
|
||||
void onUpdate(qint16 level, qint32 samples);
|
||||
void onError();
|
||||
|
||||
private:
|
||||
|
||||
friend class AudioCaptureInner;
|
||||
|
||||
QThread _captureThread;
|
||||
AudioCaptureInner *_capture;
|
||||
|
||||
};
|
||||
|
||||
AudioPlayer *audioPlayer();
|
||||
AudioCapture *audioCapture();
|
||||
|
||||
class AudioPlayerFader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AudioPlayerFader(QThread *thread);
|
||||
void processContext();
|
||||
|
||||
signals:
|
||||
@@ -139,13 +176,14 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class VoiceMessagesLoader : public QObject {
|
||||
class AudioPlayerLoader;
|
||||
class AudioPlayerLoaders : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoiceMessagesLoader(QThread *thread);
|
||||
~VoiceMessagesLoader();
|
||||
AudioPlayerLoaders(QThread *thread);
|
||||
~AudioPlayerLoaders();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -161,10 +199,43 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
struct Loader;
|
||||
typedef QMap<AudioData*, Loader*> Loaders;
|
||||
typedef QMap<AudioData*, AudioPlayerLoader*> Loaders;
|
||||
Loaders _loaders;
|
||||
|
||||
void loadError(Loaders::iterator i);
|
||||
|
||||
};
|
||||
|
||||
struct AudioCapturePrivate;
|
||||
|
||||
class AudioCaptureInner : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AudioCaptureInner(QThread *thread);
|
||||
~AudioCaptureInner();
|
||||
|
||||
signals:
|
||||
|
||||
void error();
|
||||
void update(qint16 level, qint32 samples);
|
||||
void done(QByteArray data, qint32 samples);
|
||||
|
||||
public slots:
|
||||
|
||||
void onInit();
|
||||
void onStart();
|
||||
void onStop(bool needResult);
|
||||
|
||||
void onTimeout();
|
||||
|
||||
private:
|
||||
|
||||
void writeFrame(int32 offset, int32 framesize);
|
||||
|
||||
AudioCapturePrivate *d;
|
||||
QTimer _timer;
|
||||
QByteArray _captured;
|
||||
|
||||
};
|
||||
|
||||
@@ -71,7 +71,7 @@ void AbstractBox::paintTitle(Painter &p, const QString &title, bool withShadow)
|
||||
// paint box title
|
||||
p.setFont(st::boxTitleFont->f);
|
||||
p.setPen(st::black->p);
|
||||
p.drawTextLeft(st::boxTitlePos.x(), st::boxTitlePos.y(), width() - 2 * st::boxTitlePos.x(), title);
|
||||
p.drawTextLeft(st::boxTitlePos.x(), st::boxTitlePos.y(), width(), title);
|
||||
}
|
||||
|
||||
void AbstractBox::paintGrayTitle(QPainter &p, const QString &title) {
|
||||
@@ -106,8 +106,10 @@ void AbstractBox::setMaxHeight(int32 maxHeight) {
|
||||
|
||||
void AbstractBox::resizeMaxHeight(int32 newWidth, int32 maxHeight) {
|
||||
if (width() != newWidth || _maxHeight != maxHeight) {
|
||||
QRect g(geometry());
|
||||
_maxHeight = maxHeight;
|
||||
resize(newWidth, countHeight());
|
||||
if (parentWidget()) parentWidget()->update(geometry().united(g).marginsAdded(QMargins(st::boxShadow.pxWidth(), st::boxShadow.pxHeight(), st::boxShadow.pxWidth(), st::boxShadow.pxHeight())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ _bgCount(0), _rows(0), _over(-1), _overDown(-1) {
|
||||
void BackgroundInner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
|
||||
App::WallPapers wallpapers;
|
||||
|
||||
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG), ImagePtr(st::msgBG)));
|
||||
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0)));
|
||||
const QVector<MTPWallPaper> &v(result.c_vector().v);
|
||||
for (int i = 0, l = v.size(); i < l; ++i) {
|
||||
const MTPWallPaper w(v.at(i));
|
||||
@@ -78,7 +78,7 @@ void BackgroundInner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
|
||||
}
|
||||
}
|
||||
if (thumb && full && full->type() != mtpc_photoSizeEmpty) {
|
||||
wallpapers.push_back(App::WallPaper(d.vid.v, App::image(*thumb), App::image(*full)));
|
||||
wallpapers.push_back(App::WallPaper(d.vid.v ? d.vid.v : INT_MAX, App::image(*thumb), App::image(*full)));
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
@@ -68,12 +68,12 @@ namespace {
|
||||
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 7;
|
||||
}
|
||||
|
||||
EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton) {
|
||||
EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]), _done(this, lang(lng_about_done), st::aboutCloseButton) {
|
||||
fillBlocks();
|
||||
|
||||
_blockHeight = st::emojiReplaceInnerHeight;
|
||||
|
||||
resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - st::emojiSize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height());
|
||||
resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - _esize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height());
|
||||
|
||||
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
|
||||
@@ -84,7 +84,21 @@ void EmojiBox::fillBlocks() {
|
||||
BlockRow currentRow;
|
||||
currentRow.reserve(replacesInRow);
|
||||
for (uint32 i = 0; i < replacesCount; ++i) {
|
||||
Block block(getEmoji(replaces[i].code), QString::fromUtf8(replaces[i].replace));
|
||||
EmojiPtr emoji = emojiGet(replaces[i].code);
|
||||
if (!emoji || emoji == TwoSymbolEmoji) continue;
|
||||
if (emoji->color) {
|
||||
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
|
||||
if (it != cEmojiVariants().cend()) {
|
||||
EmojiPtr replace = emojiFromKey(it.value());
|
||||
if (replace) {
|
||||
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
|
||||
emoji = replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Block block(emoji, QString::fromUtf8(replaces[i].replace));
|
||||
currentRow.push_back(block);
|
||||
if (uint32(currentRow.size()) == replacesInRow) {
|
||||
_blocks.push_back(currentRow);
|
||||
@@ -125,8 +139,7 @@ void EmojiBox::paintEvent(QPaintEvent *e) {
|
||||
int32 rowSize = i->size(), left = (width() - rowSize * st::emojiReplaceWidth) / 2;
|
||||
for (BlockRow::const_iterator j = i->cbegin(), en = i->cend(); j != en; ++j) {
|
||||
if (j->emoji) {
|
||||
QPoint pos(left + (st::emojiReplaceWidth - st::emojiSize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2);
|
||||
p.drawPixmap(pos, App::emojis(), QRect(j->emoji->x, j->emoji->y, st::emojiImgSize, st::emojiImgSize));
|
||||
p.drawPixmap(QPoint(left + (st::emojiReplaceWidth - _esize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2), App::emojisLarge(), QRect(j->emoji->x * _esize, j->emoji->y * _esize, _esize, _esize));
|
||||
}
|
||||
QRect trect(left, top + (st::emojiReplaceHeight + _blockHeight) / 2 - st::emojiTextFont->height, st::emojiReplaceWidth, st::emojiTextFont->height);
|
||||
p.drawText(trect, j->text, QTextOption(Qt::AlignHCenter | Qt::AlignTop));
|
||||
|
||||
@@ -38,6 +38,7 @@ private:
|
||||
|
||||
void fillBlocks();
|
||||
|
||||
int32 _esize;
|
||||
BottomButton _done;
|
||||
|
||||
int32 _blockHeight;
|
||||
|
||||
@@ -147,8 +147,7 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
int32 x = (width() - w) / 2, y = st::boxPadding.top() * 2 + st::boxFont->height;
|
||||
|
||||
p.fillRect(QRect(x, y, w, h), st::msgOutBG->b);
|
||||
p.fillRect(x, y + h, w, st::msgShadow, st::msgOutShadow->b);
|
||||
App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow);
|
||||
if (_thumbw) {
|
||||
int32 rf(cIntRetinaFactor());
|
||||
p.drawPixmap(QPoint(x + st::mediaPadding.left(), y + st::mediaPadding.top()), _thumb, QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf));
|
||||
|
||||
@@ -37,7 +37,7 @@ void SessionsInner::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(r, st::white->b);
|
||||
p.setFont(st::linkFont->f);
|
||||
int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip;
|
||||
int32 w = width() - 2 * x, availw = width() - 2 * xact;
|
||||
int32 w = width();
|
||||
int32 from = (r.top() >= 0) ? qFloor(r.top() / st::sessionHeight) : 0, count = _list->size();
|
||||
if (from < count) {
|
||||
int32 to = (r.bottom() >= 0 ? qFloor(r.bottom() / st::sessionHeight) : 0) + 1;
|
||||
@@ -52,7 +52,7 @@ void SessionsInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
p.setFont(st::sessionActiveFont->f);
|
||||
p.setPen(st::sessionActiveColor->p);
|
||||
p.drawTextRight(xact, st::sessionPadding.top(), availw, auth.active, auth.activeWidth);
|
||||
p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth);
|
||||
|
||||
p.setFont(st::sessionInfoFont->f);
|
||||
p.setPen(st::black->p);
|
||||
@@ -132,7 +132,7 @@ void SessionsInner::listUpdated() {
|
||||
j = _terminateButtons.insert(_list->at(i).hash, new IconedButton(this, st::sessionTerminate));
|
||||
connect(j.value(), SIGNAL(clicked()), this, SLOT(onTerminate()));
|
||||
}
|
||||
j.value()->moveToRight(st::sessionTerminateSkip, i * st::sessionHeight + st::sessionTerminateTop, width() - 2 * st::sessionTerminateSkip);
|
||||
j.value()->moveToRight(st::sessionTerminateSkip, i * st::sessionHeight + st::sessionTerminateTop, width());
|
||||
}
|
||||
for (TerminateButtons::iterator i = _terminateButtons.begin(); i != _terminateButtons.cend();) {
|
||||
if (i.value()->y() >= 0) {
|
||||
@@ -175,7 +175,7 @@ _terminateAll(this, lang(lng_sessions_terminate_all)), _terminateBox(0), _shortP
|
||||
void SessionsBox::resizeEvent(QResizeEvent *e) {
|
||||
ScrollableBox::resizeEvent(e);
|
||||
_done.move(0, height() - _done.height());
|
||||
_terminateAll.moveToRight(st::sessionPadding.left(), st::boxTitleHeight + st::sessionHeight + st::boxTitlePos.y() + st::boxTitleFont->ascent - st::linkFont->ascent, width() - 2 * st::sessionPadding.left());
|
||||
_terminateAll.moveToRight(st::sessionPadding.left(), st::boxTitleHeight + st::sessionHeight + st::boxTitlePos.y() + st::boxTitleFont->ascent - st::linkFont->ascent, width());
|
||||
}
|
||||
|
||||
void SessionsBox::hideAll() {
|
||||
@@ -212,7 +212,7 @@ void SessionsBox::paintEvent(QPaintEvent *e) {
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
|
||||
} else {
|
||||
int32 x = st::sessionPadding.left();
|
||||
int32 w = width() - x - st::sessionPadding.right();
|
||||
int32 w = width();
|
||||
|
||||
p.setFont(st::sessionNameFont->f);
|
||||
p.setPen(st::black->p);
|
||||
|
||||
293
Telegram/SourceFiles/boxes/stickersetbox.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "lang.h"
|
||||
|
||||
#include "stickersetbox.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
#include "settingswidget.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) :
|
||||
_loaded(false), _setId(0), _setAccess(0), _bottom(0),
|
||||
_input(set), _installRequest(0) {
|
||||
switch (set.type()) {
|
||||
case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break;
|
||||
case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break;
|
||||
}
|
||||
MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&StickerSetInner::gotSet), rpcFail(&StickerSetInner::failedSet));
|
||||
cSetLastStickersUpdate(0);
|
||||
App::main()->updateStickers();
|
||||
}
|
||||
|
||||
void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
|
||||
_pack.clear();
|
||||
if (set.type() == mtpc_messages_stickerSet) {
|
||||
const MTPDmessages_stickerSet &d(set.c_messages_stickerSet());
|
||||
const QVector<MTPDocument> &v(d.vdocuments.c_vector().v);
|
||||
_pack.reserve(v.size());
|
||||
for (int32 i = 0, l = v.size(); i < l; ++i) {
|
||||
DocumentData *doc = App::feedDocument(v.at(i));
|
||||
if (!doc || !doc->sticker) continue;
|
||||
|
||||
_pack.push_back(doc);
|
||||
}
|
||||
if (d.vset.type() == mtpc_stickerSet) {
|
||||
const MTPDstickerSet &s(d.vset.c_stickerSet());
|
||||
_setTitle = qs(s.vtitle);
|
||||
_title = st::boxTitleFont->m.elidedText(_setTitle, Qt::ElideRight, width() - st::btnStickersClose.width - st::boxTitlePos.x());
|
||||
_setShortName = qs(s.vshort_name);
|
||||
_setId = s.vid.v;
|
||||
_setAccess = s.vaccess_hash.v;
|
||||
}
|
||||
}
|
||||
|
||||
if (_pack.isEmpty() || _setShortName.isEmpty()) {
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
|
||||
} else {
|
||||
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
|
||||
resize(st::stickersPadding + StickerPanPerRow * st::stickersSize.width(), rows * st::stickersSize.height() + st::stickersAddOrShare);
|
||||
}
|
||||
_loaded = true;
|
||||
|
||||
emit updateButtons();
|
||||
}
|
||||
|
||||
bool StickerSetInner::failedSet(const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
_loaded = true;
|
||||
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StickerSetInner::installDone(const MTPBool &result) {
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
|
||||
sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName)).value().stickers = _pack;
|
||||
int32 index = cStickerSetsOrder().indexOf(_setId);
|
||||
if (index > 0) {
|
||||
cRefStickerSetsOrder().removeAt(index);
|
||||
cRefStickerSetsOrder().push_front(_setId);
|
||||
} else if (index < 0) {
|
||||
cRefStickerSetsOrder().push_front(_setId);
|
||||
}
|
||||
|
||||
StickerSets::iterator custom = sets.find(CustomStickerSetId);
|
||||
if (custom != sets.cend()) {
|
||||
for (int32 i = 0, l = _pack.size(); i < l; ++i) {
|
||||
custom->stickers.removeOne(_pack.at(i));
|
||||
}
|
||||
if (custom->stickers.isEmpty()) {
|
||||
sets.erase(custom);
|
||||
}
|
||||
}
|
||||
cSetStickersHash(QByteArray());
|
||||
Local::writeStickers();
|
||||
emit installed(_setId);
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
bool StickerSetInner::installFailed(const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StickerSetInner::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
Painter p(this);
|
||||
|
||||
if (_pack.isEmpty()) return;
|
||||
|
||||
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
|
||||
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
|
||||
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
for (int32 j = 0; j < StickerPanPerRow; ++j) {
|
||||
int32 index = i * StickerPanPerRow + j;
|
||||
if (index >= _pack.size()) break;
|
||||
|
||||
DocumentData *doc = _pack.at(index);
|
||||
QPoint pos(st::stickerPanPadding + j * st::stickersSize.width(), i * st::stickersSize.height());
|
||||
|
||||
bool goodThumb = !doc->thumb->isNull() && ((doc->thumb->width() >= 128) || (doc->thumb->height() >= 128));
|
||||
if (goodThumb) {
|
||||
doc->thumb->load();
|
||||
} else {
|
||||
bool already = !doc->already().isEmpty(), hasdata = !doc->data.isEmpty();
|
||||
if (!doc->loader && doc->status != FileFailed && !already && !hasdata) {
|
||||
doc->save(QString());
|
||||
}
|
||||
if (doc->sticker->img->isNull() && (already || hasdata)) {
|
||||
if (already) {
|
||||
doc->sticker->img = ImagePtr(doc->already());
|
||||
} else {
|
||||
doc->sticker->img = ImagePtr(doc->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float64 coef = qMin((st::stickersSize.width() - st::msgRadius * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::msgRadius * 2) / float64(doc->dimensions.height()));
|
||||
if (coef > 1) coef = 1;
|
||||
int32 w = qRound(coef * doc->dimensions.width()), h = qRound(coef * doc->dimensions.height());
|
||||
if (w < 1) w = 1;
|
||||
if (h < 1) h = 1;
|
||||
QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
|
||||
if (goodThumb) {
|
||||
p.drawPixmapLeft(ppos, width(), doc->thumb->pix(w, h));
|
||||
} else if (!doc->sticker->img->isNull()) {
|
||||
p.drawPixmapLeft(ppos, width(), doc->sticker->img->pix(w, h));
|
||||
}
|
||||
}
|
||||
}
|
||||
p.fillRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare, st::emojiPanHeaderBg->b);
|
||||
}
|
||||
|
||||
void StickerSetInner::setScrollBottom(int32 bottom) {
|
||||
if (bottom == _bottom) return;
|
||||
|
||||
QRegion upd = QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare);
|
||||
_bottom = bottom;
|
||||
upd += QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare);
|
||||
repaint(upd);
|
||||
}
|
||||
|
||||
bool StickerSetInner::loaded() const {
|
||||
return _loaded && !_pack.isEmpty();
|
||||
}
|
||||
|
||||
int32 StickerSetInner::notInstalled() const {
|
||||
return (_loaded && (cStickerSets().constFind(_setId) == cStickerSets().cend())) ? _pack.size() : 0;
|
||||
}
|
||||
|
||||
QString StickerSetInner::title() const {
|
||||
return _loaded ? (_pack.isEmpty() ? lang(lng_attach_failed) : _title) : lang(lng_contacts_loading);
|
||||
}
|
||||
|
||||
QString StickerSetInner::shortName() const {
|
||||
return _setShortName;
|
||||
}
|
||||
|
||||
void StickerSetInner::install() {
|
||||
if (_installRequest) return;
|
||||
_installRequest = MTP::send(MTPmessages_InstallStickerSet(_input), rpcDone(&StickerSetInner::installDone), rpcFail(&StickerSetInner::installFailed));
|
||||
}
|
||||
|
||||
StickerSetInner::~StickerSetInner() {
|
||||
}
|
||||
|
||||
StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll), _inner(set),
|
||||
_close(this, st::btnStickersClose),
|
||||
_addStickers(this, lng_stickers_add_pack(lt_count, 0), st::btnStickersAdd),
|
||||
_shareStickers(this, lang(lng_stickers_share_pack), st::btnStickersAdd) {
|
||||
resize(st::stickersWidth, height());
|
||||
setMaxHeight(st::stickersMaxHeight);
|
||||
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
|
||||
|
||||
init(&_inner, 0, st::boxFont->height + st::newGroupNamePadding.top() + st::newGroupNamePadding.bottom());
|
||||
|
||||
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
connect(&_addStickers, SIGNAL(clicked()), this, SLOT(onAddStickers()));
|
||||
connect(&_shareStickers, SIGNAL(clicked()), this, SLOT(onShareStickers()));
|
||||
|
||||
connect(&_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons()));
|
||||
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
|
||||
connect(&_inner, SIGNAL(installed(uint64)), this, SIGNAL(installed(uint64)));
|
||||
|
||||
onStickersUpdated();
|
||||
|
||||
onScroll();
|
||||
|
||||
prepare();
|
||||
}
|
||||
|
||||
void StickerSetBox::onStickersUpdated() {
|
||||
showAll();
|
||||
}
|
||||
|
||||
void StickerSetBox::onAddStickers() {
|
||||
_inner.install();
|
||||
}
|
||||
|
||||
void StickerSetBox::onShareStickers() {
|
||||
QString url = qsl("https://telegram.me/addstickers/") + _inner.shortName();
|
||||
DEBUG_LOG(("Setting text to clipboard from stickerset box: %1").arg(url));
|
||||
QApplication::clipboard()->setText(url);
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_copied), true), true);
|
||||
}
|
||||
|
||||
void StickerSetBox::onUpdateButtons() {
|
||||
if (!_close.isHidden()) showAll();
|
||||
}
|
||||
|
||||
void StickerSetBox::onScroll() {
|
||||
_inner.setScrollBottom(_scroll.scrollTop() + _scroll.height());
|
||||
}
|
||||
|
||||
void StickerSetBox::hideAll() {
|
||||
ScrollableBox::hideAll();
|
||||
_close.hide();
|
||||
_addStickers.hide();
|
||||
_shareStickers.hide();
|
||||
}
|
||||
|
||||
void StickerSetBox::showAll() {
|
||||
ScrollableBox::showAll();
|
||||
_close.show();
|
||||
int32 cnt = _inner.notInstalled();
|
||||
if (_inner.loaded()) {
|
||||
if (_inner.notInstalled()) {
|
||||
_addStickers.setText(lng_stickers_add_pack(lt_count, cnt));
|
||||
_addStickers.show();
|
||||
_addStickers.raise();
|
||||
_shareStickers.hide();
|
||||
} else {
|
||||
_shareStickers.show();
|
||||
_shareStickers.raise();
|
||||
_addStickers.hide();
|
||||
}
|
||||
} else {
|
||||
_addStickers.hide();
|
||||
_shareStickers.hide();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void StickerSetBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
if (paint(p)) return;
|
||||
|
||||
paintTitle(p, _inner.title(), false);
|
||||
}
|
||||
|
||||
void StickerSetBox::resizeEvent(QResizeEvent *e) {
|
||||
ScrollableBox::resizeEvent(e);
|
||||
_inner.resize(width(), _inner.height());
|
||||
_close.moveToRight(0, 0, width());
|
||||
_addStickers.move((width() - _addStickers.width()) / 2, height() - (st::stickersAddOrShare + _addStickers.height()) / 2);
|
||||
_shareStickers.move((width() - _shareStickers.width()) / 2, height() - (st::stickersAddOrShare + _shareStickers.height()) / 2);
|
||||
}
|
||||
100
Telegram/SourceFiles/boxes/stickersetbox.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "abstractbox.h"
|
||||
|
||||
class StickerSetInner : public QWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StickerSetInner(const MTPInputStickerSet &set);
|
||||
|
||||
void init();
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
bool loaded() const;
|
||||
int32 notInstalled() const;
|
||||
QString title() const;
|
||||
QString shortName() const;
|
||||
|
||||
void setScrollBottom(int32 bottom);
|
||||
void install();
|
||||
|
||||
~StickerSetInner();
|
||||
|
||||
signals:
|
||||
|
||||
void updateButtons();
|
||||
void installed(uint64 id);
|
||||
|
||||
private:
|
||||
|
||||
void gotSet(const MTPmessages_StickerSet &set);
|
||||
bool failedSet(const RPCError &error);
|
||||
|
||||
void installDone(const MTPBool &result);
|
||||
bool installFailed(const RPCError &error);
|
||||
|
||||
StickerPack _pack;
|
||||
bool _loaded;
|
||||
uint64 _setId, _setAccess;
|
||||
QString _title, _setTitle, _setShortName;
|
||||
|
||||
int32 _bottom;
|
||||
MTPInputStickerSet _input;
|
||||
|
||||
mtpRequestId _installRequest;
|
||||
};
|
||||
|
||||
class StickerSetBox : public ScrollableBox, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StickerSetBox(const MTPInputStickerSet &set);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
||||
public slots:
|
||||
|
||||
void onStickersUpdated();
|
||||
void onAddStickers();
|
||||
void onShareStickers();
|
||||
void onUpdateButtons();
|
||||
|
||||
void onScroll();
|
||||
|
||||
signals:
|
||||
|
||||
void installed(uint64 id);
|
||||
|
||||
protected:
|
||||
|
||||
void hideAll();
|
||||
void showAll();
|
||||
|
||||
private:
|
||||
|
||||
StickerSetInner _inner;
|
||||
IconedButton _close;
|
||||
FlatButton _addStickers, _shareStickers;
|
||||
};
|
||||
@@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const int32 AppVersion = 8004;
|
||||
static const wchar_t *AppVersionStr = L"0.8.4";
|
||||
static const bool DevChannel = false;
|
||||
static const int32 AppVersion = 8018;
|
||||
static const wchar_t *AppVersionStr = L"0.8.18";
|
||||
static const bool DevChannel = true;
|
||||
|
||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||
static const wchar_t *AppName = L"Telegram Desktop";
|
||||
@@ -86,8 +86,12 @@ enum {
|
||||
AudioCheckPositionDelta = 4800, // update position called each 4800 samples
|
||||
AudioFadeTimeout = 10, // 10ms
|
||||
AudioFadeDuration = 500,
|
||||
AudioVoiceMsgSkip = 400, // 200ms
|
||||
AudioVoiceMsgFade = 300, // 300ms
|
||||
AudioPreloadSamples = 5 * 48000, // preload next part if less than 5 seconds remains
|
||||
AudioVoiceMsgFrequency = 48000, // 48 kHz
|
||||
AudioVoiceMsgMaxLength = 100 * 60, // 100 minutes
|
||||
AudioVoiceMsgUpdateView = 100, // 100ms
|
||||
AudioVoiceMsgChannels = 2, // stereo
|
||||
AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers
|
||||
AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded
|
||||
@@ -101,9 +105,10 @@ enum {
|
||||
ZoomToScreenLevel = 1024, // just constant
|
||||
|
||||
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
|
||||
EmojiPadPerRow = 7,
|
||||
EmojiPadRowsPerPage = 6,
|
||||
StickerPadPerRow = 3,
|
||||
EmojiPanPerRow = 7,
|
||||
EmojiPanRowsPerPage = 6,
|
||||
StickerPanPerRow = 5,
|
||||
StickerPanRowsPerPage = 4,
|
||||
StickersUpdateTimeout = 3600000, // update not more than once in an hour
|
||||
|
||||
SearchPeopleLimit = 5,
|
||||
@@ -255,6 +260,8 @@ static const char *DefaultCountry = "US";
|
||||
static const char *DefaultLanguage = "en";
|
||||
|
||||
enum {
|
||||
DefaultChatBackground = 21,
|
||||
|
||||
DialogsFirstLoad = 20, // first dialogs part size requested
|
||||
DialogsPerPage = 40, // next dialogs part size
|
||||
|
||||
@@ -279,8 +286,8 @@ enum {
|
||||
UploadRequestInterval = 500, // one part each half second, if not uploaded faster
|
||||
|
||||
MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created
|
||||
NoUpdatesTimeout = 180 * 1000, // if nothing is received in 3 min we getDifference
|
||||
NoUpdatesAfterSleepTimeout = 60 * 1000, // if nothing is received in 1 min when was a sleepmode we getDifference
|
||||
NoUpdatesTimeout = 60 * 1000, // if nothing is received in 1 min we ping
|
||||
NoUpdatesAfterSleepTimeout = 60 * 1000, // if nothing is received in 1 min when was a sleepmode we ping
|
||||
WaitForSkippedTimeout = 1000, // 1s wait for skipped seq or pts in updates
|
||||
|
||||
MemoryForImageCache = 64 * 1024 * 1024, // after 64mb of unpacked images we try to clear some memory
|
||||
|
||||
@@ -67,11 +67,11 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
bool trivial = (rect() == r);
|
||||
|
||||
QPainter p(this);
|
||||
Painter p(this);
|
||||
if (!trivial) {
|
||||
p.setClipRect(r);
|
||||
}
|
||||
|
||||
|
||||
if (_state == DefaultState) {
|
||||
int32 otherStart = dialogs.list.count * st::dlgHeight;
|
||||
PeerData *active = App::main()->activePeer(), *selected = sel ? sel->history->peer : 0;
|
||||
@@ -1122,7 +1122,7 @@ void DialogsListWidget::saveRecentHashtags(const QString &text) {
|
||||
}
|
||||
}
|
||||
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||
Local::readRecentStickers();
|
||||
Local::readRecentHashtags();
|
||||
recent = cRecentSearchHashtags();
|
||||
}
|
||||
found = true;
|
||||
|
||||
@@ -25,25 +25,32 @@ class Dropdown : public TWidget, public Animated {
|
||||
|
||||
public:
|
||||
|
||||
Dropdown(QWidget *parent);
|
||||
Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef);
|
||||
|
||||
IconedButton *addButton(IconedButton *button);
|
||||
void resetButtons();
|
||||
void updateButtons();
|
||||
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
void enterEvent(QEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void keyPressEvent(QKeyEvent *e);
|
||||
void otherEnter();
|
||||
void otherLeave();
|
||||
|
||||
void fastHide();
|
||||
void ignoreShow(bool ignore = true);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *e);
|
||||
|
||||
signals:
|
||||
|
||||
void hiding();
|
||||
|
||||
public slots:
|
||||
|
||||
void hideStart();
|
||||
@@ -52,13 +59,21 @@ public slots:
|
||||
void showStart();
|
||||
void onWndActiveChanged();
|
||||
|
||||
void buttonStateChanged(int oldState, ButtonStateChangeSource source);
|
||||
|
||||
private:
|
||||
|
||||
void adjustButtons();
|
||||
|
||||
bool _ignore;
|
||||
|
||||
typedef QVector<IconedButton*> Buttons;
|
||||
Buttons _buttons;
|
||||
|
||||
int32 _selected;
|
||||
|
||||
const style::dropdown &_st;
|
||||
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
|
||||
@@ -117,7 +132,70 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class EmojiPanInner : public QWidget, public Animated {
|
||||
static const int EmojiColorsCount = 5;
|
||||
|
||||
class EmojiColorPicker : public TWidget, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
EmojiColorPicker(QWidget *parent);
|
||||
|
||||
void showEmoji(uint32 code);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void enterEvent(QEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
void showStart();
|
||||
|
||||
void clearSelection(bool fast = false);
|
||||
|
||||
public slots:
|
||||
|
||||
void hideStart(bool fast = false);
|
||||
|
||||
signals:
|
||||
|
||||
void emojiSelected(EmojiPtr emoji);
|
||||
void hidden();
|
||||
|
||||
private:
|
||||
|
||||
void drawVariant(Painter &p, int variant);
|
||||
|
||||
void updateSelected();
|
||||
|
||||
bool _ignoreShow;
|
||||
|
||||
EmojiPtr _variants[EmojiColorsCount + 1];
|
||||
|
||||
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
|
||||
EmojiAnimations _emojiAnimations;
|
||||
|
||||
float64 _hovers[EmojiColorsCount + 1];
|
||||
|
||||
int32 _selected, _pressedSel;
|
||||
QPoint _lastMousePos;
|
||||
|
||||
bool _hiding;
|
||||
QPixmap _cache;
|
||||
|
||||
anim::fvalue a_opacity;
|
||||
|
||||
QTimer _hideTimer;
|
||||
|
||||
BoxShadow _shadow;
|
||||
|
||||
};
|
||||
|
||||
static const int32 SwitcherSelected = (INT_MAX / 2);
|
||||
|
||||
class EmojiPanInner : public TWidget, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -130,39 +208,131 @@ public:
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
void enterFromChildEvent(QEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
void hideFinish();
|
||||
|
||||
void showEmojiPack(DBIEmojiTab packIndex);
|
||||
|
||||
void clearSelection(bool fast = false);
|
||||
|
||||
DBIEmojiTab currentTab(int yOffset) const;
|
||||
|
||||
void refreshRecent();
|
||||
|
||||
void setScrollTop(int top);
|
||||
|
||||
public slots:
|
||||
|
||||
void updateSelected();
|
||||
void onSaveConfig();
|
||||
|
||||
void onShowPicker();
|
||||
void onPickerHidden();
|
||||
void onColorSelected(EmojiPtr emoji);
|
||||
|
||||
signals:
|
||||
|
||||
void emojiSelected(EmojiPtr emoji);
|
||||
void stickerSelected(DocumentData *sticker);
|
||||
void selected(EmojiPtr emoji);
|
||||
|
||||
void switchToStickers();
|
||||
|
||||
void scrollToY(int y);
|
||||
void disableScroll(bool dis);
|
||||
|
||||
private:
|
||||
|
||||
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
|
||||
EmojiAnimations _emojiAnimations;
|
||||
int32 countHeight();
|
||||
void selectEmoji(EmojiPtr emoji);
|
||||
|
||||
StickerPack _stickers;
|
||||
QVector<bool> _isUserGen;
|
||||
QVector<EmojiPtr> _emojis;
|
||||
QVector<float64> _hovers;
|
||||
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
|
||||
Animations _animations;
|
||||
|
||||
DBIEmojiTab _tab;
|
||||
int32 _selected, _xSelected, _pressedSel, _xPressedSel;
|
||||
int32 _top, _counts[emojiTabCount];
|
||||
|
||||
QVector<EmojiPtr> _emojis[emojiTabCount];
|
||||
QVector<float64> _hovers[emojiTabCount];
|
||||
|
||||
int32 _esize;
|
||||
|
||||
int32 _selected, _pressedSel, _pickerSel;
|
||||
QPoint _lastMousePos;
|
||||
|
||||
QTimer _saveConfigTimer;
|
||||
|
||||
EmojiColorPicker _picker;
|
||||
QTimer _showPickerTimer;
|
||||
|
||||
float64 _switcherHover;
|
||||
int32 _stickersWidth;
|
||||
};
|
||||
|
||||
class StickerPanInner : public TWidget, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StickerPanInner(QWidget *parent = 0);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
void enterFromChildEvent(QEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
void showStickerSet(uint64 setId);
|
||||
|
||||
void clearSelection(bool fast = false);
|
||||
|
||||
void refreshStickers();
|
||||
void refreshRecent(bool resize = true);
|
||||
|
||||
void setScrollTop(int top);
|
||||
void preloadImages();
|
||||
|
||||
public slots:
|
||||
|
||||
void updateSelected();
|
||||
|
||||
signals:
|
||||
|
||||
void selected(DocumentData *sticker);
|
||||
void removing(uint64 setId);
|
||||
|
||||
void switchToEmoji();
|
||||
|
||||
void scrollToY(int y);
|
||||
void disableScroll(bool dis);
|
||||
|
||||
private:
|
||||
|
||||
void appendSet(uint64 setId);
|
||||
|
||||
int32 countHeight();
|
||||
void selectEmoji(EmojiPtr emoji);
|
||||
|
||||
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
|
||||
Animations _animations;
|
||||
|
||||
int32 _top;
|
||||
|
||||
QList<QString> _titles;
|
||||
QList<uint64> _setIds;
|
||||
QList<StickerPack> _sets;
|
||||
QList<QVector<float64> > _hovers;
|
||||
|
||||
int32 _selected, _pressedSel;
|
||||
QPoint _lastMousePos;
|
||||
|
||||
float64 _switcherHover;
|
||||
int32 _emojiWidth;
|
||||
};
|
||||
|
||||
class EmojiPan : public TWidget, public Animated {
|
||||
@@ -187,9 +357,12 @@ public:
|
||||
bool animStep(float64 ms);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *e);
|
||||
void stickersInstalled(uint64 setId);
|
||||
|
||||
public slots:
|
||||
|
||||
void refreshStickers();
|
||||
|
||||
void hideStart();
|
||||
void hideFinish();
|
||||
|
||||
@@ -197,6 +370,12 @@ public slots:
|
||||
void onWndActiveChanged();
|
||||
|
||||
void onTabChange();
|
||||
void onScroll();
|
||||
void onSwitch();
|
||||
|
||||
void onRemoveSet(uint64 setId);
|
||||
void onRemoveSetSure();
|
||||
void onDelayedHide();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -206,9 +385,13 @@ signals:
|
||||
|
||||
private:
|
||||
|
||||
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
|
||||
|
||||
void showAll();
|
||||
void hideAll();
|
||||
|
||||
bool _noTabUpdate;
|
||||
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
QPixmap _cache;
|
||||
@@ -219,11 +402,20 @@ private:
|
||||
|
||||
BoxShadow _shadow;
|
||||
|
||||
FlatRadiobutton _recent, _people, _nature, _objects, _places, _symbols, _stickers;
|
||||
FlatRadiobutton _recent, _people, _nature, _food, _celebration, _activity, _travel, _objects;
|
||||
|
||||
int32 _emojiPack;
|
||||
ScrollArea _scroll;
|
||||
EmojiPanInner _inner;
|
||||
bool _stickersShown;
|
||||
QPixmap _fromCache, _toCache;
|
||||
anim::ivalue a_fromCoord, a_toCoord;
|
||||
anim::fvalue a_fromAlpha, a_toAlpha;
|
||||
uint64 _moveStart;
|
||||
|
||||
ScrollArea e_scroll;
|
||||
EmojiPanInner e_inner;
|
||||
ScrollArea s_scroll;
|
||||
StickerPanInner s_inner;
|
||||
|
||||
uint64 _removingSetId;
|
||||
|
||||
};
|
||||
|
||||
@@ -336,97 +528,3 @@ private:
|
||||
BoxShadow _shadow;
|
||||
|
||||
};
|
||||
|
||||
//class StickerPanInner : public QWidget, public Animated {
|
||||
// Q_OBJECT
|
||||
//
|
||||
//public:
|
||||
//
|
||||
// StickerPanInner(QWidget *parent = 0);
|
||||
//
|
||||
// void paintEvent(QPaintEvent *e);
|
||||
//
|
||||
// void mousePressEvent(QMouseEvent *e);
|
||||
// void mouseReleaseEvent(QMouseEvent *e);
|
||||
// void mouseMoveEvent(QMouseEvent *e);
|
||||
// void leaveEvent(QEvent *e);
|
||||
//
|
||||
// bool animStep(float64 ms);
|
||||
//
|
||||
// void showStickerPack(EmojiPtr emoji);
|
||||
// bool hasContent() const;
|
||||
//
|
||||
//public slots:
|
||||
//
|
||||
// void updateSelected();
|
||||
//
|
||||
//signals:
|
||||
//
|
||||
// void stickerSelected(DocumentData *sticker);
|
||||
//
|
||||
//private:
|
||||
//
|
||||
// typedef QMap<int32, uint64> StickerAnimations; // index - showing, -index - hiding
|
||||
// StickerAnimations _stickerAnimations;
|
||||
//
|
||||
// StickerPack _stickers;
|
||||
// QVector<float64> _hovers;
|
||||
//
|
||||
// EmojiPtr _emoji;
|
||||
// int32 _selected, _pressedSel;
|
||||
// QPoint _lastMousePos;
|
||||
//
|
||||
//};
|
||||
//
|
||||
//class StickerPan : public TWidget, public Animated {
|
||||
// Q_OBJECT
|
||||
//
|
||||
//public:
|
||||
//
|
||||
// StickerPan(QWidget *parent);
|
||||
//
|
||||
// void setStickerPack(EmojiPtr emoji, bool show);
|
||||
// void paintEvent(QPaintEvent *e);
|
||||
//
|
||||
// void enterEvent(QEvent *e);
|
||||
// void leaveEvent(QEvent *e);
|
||||
// void otherEnter();
|
||||
// void otherLeave();
|
||||
//
|
||||
// void fastHide();
|
||||
//
|
||||
// bool animStep(float64 ms);
|
||||
//
|
||||
// bool eventFilter(QObject *obj, QEvent *e);
|
||||
//
|
||||
//public slots:
|
||||
//
|
||||
// void hideStart();
|
||||
// void hideFinish();
|
||||
//
|
||||
// void showStart();
|
||||
//
|
||||
//signals:
|
||||
//
|
||||
// void stickerSelected(DocumentData *sticker);
|
||||
//
|
||||
//private:
|
||||
//
|
||||
// void showAll();
|
||||
// void hideAll();
|
||||
//
|
||||
// int32 _width, _height;
|
||||
// bool _hiding;
|
||||
// QPixmap _cache;
|
||||
//
|
||||
// anim::fvalue a_opacity;
|
||||
//
|
||||
// QTimer _hideTimer;
|
||||
//
|
||||
// BoxShadow _shadow;
|
||||
//
|
||||
// EmojiPtr _emoji;
|
||||
// ScrollArea _scroll;
|
||||
// StickerPanInner _inner;
|
||||
//
|
||||
//};
|
||||
|
||||
@@ -38,8 +38,12 @@ void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
|
||||
}
|
||||
document->status = FileUploading;
|
||||
if (!media.file.isEmpty()) {
|
||||
document->location = FileLocation(mtpc_storage_filePartial, media.file);
|
||||
document->location = FileLocation(StorageFilePartial, media.file);
|
||||
}
|
||||
} else if (media.type == ToPrepareAudio) {
|
||||
AudioData *audio = App::feedAudio(media.audio);
|
||||
audio->status = FileUploading;
|
||||
audio->data = media.data;
|
||||
}
|
||||
queue.insert(msgId, File(media));
|
||||
sendNext();
|
||||
@@ -56,6 +60,12 @@ void FileUploader::currentFailed() {
|
||||
doc->status = FileFailed;
|
||||
}
|
||||
emit documentFailed(j.key());
|
||||
} else if (j->media.type == ToPrepareAudio) {
|
||||
AudioData *audio = App::audio(j->media.id);
|
||||
if (audio->status == FileUploading) {
|
||||
audio->status = FileFailed;
|
||||
}
|
||||
emit audioFailed(j.key());
|
||||
}
|
||||
queue.erase(j);
|
||||
}
|
||||
@@ -112,7 +122,7 @@ void FileUploader::sendNext() {
|
||||
emit photoReady(uploading, MTP_inputFile(MTP_long(i->media.id), MTP_int(i->partsCount), MTP_string(i->media.filename), MTP_string(i->media.jpeg_md5)));
|
||||
} else if (i->media.type == ToPrepareDocument) {
|
||||
QByteArray docMd5(32, Qt::Uninitialized);
|
||||
hashMd5Hex(i->docHash.result(), docMd5.data());
|
||||
hashMd5Hex(i->md5Hash.result(), docMd5.data());
|
||||
|
||||
MTPInputFile doc = (i->docSize > UseBigFilesFrom) ? MTP_inputFileBig(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename)) : MTP_inputFile(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename), MTP_string(docMd5));
|
||||
if (i->partsCount) {
|
||||
@@ -120,6 +130,12 @@ void FileUploader::sendNext() {
|
||||
} else {
|
||||
emit documentReady(uploading, doc);
|
||||
}
|
||||
} else if (i->media.type == ToPrepareAudio) {
|
||||
QByteArray audioMd5(32, Qt::Uninitialized);
|
||||
hashMd5Hex(i->md5Hash.result(), audioMd5.data());
|
||||
|
||||
MTPInputFile audio = (i->docSize > UseBigFilesFrom) ? MTP_inputFileBig(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename)) : MTP_inputFile(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename), MTP_string(audioMd5));
|
||||
emit audioReady(uploading, audio);
|
||||
}
|
||||
queue.remove(uploading);
|
||||
uploading = 0;
|
||||
@@ -139,12 +155,12 @@ void FileUploader::sendNext() {
|
||||
}
|
||||
toSend = i->docFile->read(i->docPartSize);
|
||||
if (i->docSize <= UseBigFilesFrom) {
|
||||
i->docHash.feed(toSend.constData(), toSend.size());
|
||||
i->md5Hash.feed(toSend.constData(), toSend.size());
|
||||
}
|
||||
} else {
|
||||
toSend = i->media.data.mid(i->docSentParts * i->docPartSize, i->docPartSize);
|
||||
if (i->media.type == ToPrepareDocument && i->docSentParts <= UseBigFilesFrom) {
|
||||
i->docHash.feed(toSend.constData(), toSend.size());
|
||||
if ((i->media.type == ToPrepareDocument || i->media.type == ToPrepareAudio) && i->docSentParts <= UseBigFilesFrom) {
|
||||
i->md5Hash.feed(toSend.constData(), toSend.size());
|
||||
}
|
||||
}
|
||||
if (toSend.size() > i->docPartSize || (toSend.size() < i->docPartSize && i->docSentParts + 1 != i->docPartsCount)) {
|
||||
@@ -249,6 +265,14 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
||||
}
|
||||
}
|
||||
emit documentProgress(k.key());
|
||||
} else if (k->media.type == ToPrepareAudio) {
|
||||
AudioData *audio = App::audio(k->media.id);
|
||||
if (audio->status == FileUploading) {
|
||||
audio->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize;
|
||||
if (audio->uploadOffset > audio->size) {
|
||||
audio->uploadOffset = audio->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,19 +45,22 @@ signals:
|
||||
void photoReady(MsgId msgId, const MTPInputFile &file);
|
||||
void documentReady(MsgId msgId, const MTPInputFile &file);
|
||||
void thumbDocumentReady(MsgId msgId, const MTPInputFile &file, const MTPInputFile &thumb);
|
||||
void audioReady(MsgId msgId, const MTPInputFile &file);
|
||||
|
||||
void photoProgress(MsgId msgId);
|
||||
void documentProgress(MsgId msgId);
|
||||
void audioProgress(MsgId msgId);
|
||||
|
||||
void photoFailed(MsgId msgId);
|
||||
void documentFailed(MsgId msgId);
|
||||
void audioFailed(MsgId msgId);
|
||||
|
||||
private:
|
||||
|
||||
struct File {
|
||||
File(const ReadyLocalMedia &media) : media(media), docSentParts(0) {
|
||||
partsCount = media.parts.size();
|
||||
if (media.type == ToPrepareDocument) {
|
||||
if (media.type == ToPrepareDocument || media.type == ToPrepareAudio) {
|
||||
docSize = media.file.isEmpty() ? media.data.size() : media.filesize;
|
||||
if (docSize >= 1024 * 1024 || !setPartSize(DocumentUploadPartSize0)) {
|
||||
if (docSize > 32 * 1024 * 1024 || !setPartSize(DocumentUploadPartSize1)) {
|
||||
@@ -83,12 +86,13 @@ private:
|
||||
ReadyLocalMedia media;
|
||||
int32 partsCount;
|
||||
|
||||
HashMd5 md5Hash;
|
||||
|
||||
QSharedPointer<QFile> docFile;
|
||||
int32 docSentParts;
|
||||
int32 docSize;
|
||||
int32 docPartSize;
|
||||
int32 docPartsCount;
|
||||
HashMd5 docHash;
|
||||
};
|
||||
typedef QMap<MsgId, File> Queue;
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "animation.h"
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
|
||||
namespace {
|
||||
AnimationManager *manager = 0;
|
||||
@@ -78,6 +80,11 @@ namespace anim {
|
||||
manager->start(obj);
|
||||
}
|
||||
|
||||
void step(Animated *obj) {
|
||||
if (!manager) return;
|
||||
manager->step(obj);
|
||||
}
|
||||
|
||||
void stop(Animated *obj) {
|
||||
if (!manager) return;
|
||||
manager->stop(obj);
|
||||
@@ -94,3 +101,135 @@ namespace anim {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool AnimatedGif::animStep(float64 ms) {
|
||||
int32 f = frame;
|
||||
while (f < images.size() && ms > delays[f]) {
|
||||
++f;
|
||||
if (f == images.size() && images.size() < framesCount) {
|
||||
if (reader->read(&img)) {
|
||||
int64 d = reader->nextImageDelay(), delay = delays[f - 1];
|
||||
if (!d) d = 1;
|
||||
delay += d;
|
||||
if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
images.push_back(img);
|
||||
frames.push_back(QPixmap());
|
||||
delays.push_back(delay);
|
||||
for (int32 i = 0; i < images.size(); ++i) {
|
||||
if (!images[i].isNull() || !frames[i].isNull()) {
|
||||
images[i] = QImage();
|
||||
frames[i] = QPixmap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
framesCount = images.size();
|
||||
}
|
||||
}
|
||||
if (f == images.size()) {
|
||||
if (!duration) {
|
||||
duration = delays.isEmpty() ? 1 : delays.back();
|
||||
}
|
||||
|
||||
f = 0;
|
||||
for (int32 i = 0, s = delays.size() - 1; i <= s; ++i) {
|
||||
delays[i] += duration;
|
||||
}
|
||||
if (images[f].isNull()) {
|
||||
QString fname = reader->fileName();
|
||||
delete reader;
|
||||
reader = new QImageReader(fname);
|
||||
}
|
||||
}
|
||||
if (images[f].isNull() && reader->read(&img)) {
|
||||
if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
images[f] = img;
|
||||
frames[f] = QPixmap();
|
||||
}
|
||||
}
|
||||
if (frame != f) {
|
||||
frame = f;
|
||||
if (msg && App::main()) {
|
||||
App::main()->msgUpdated(msg->history()->peer->id, msg);
|
||||
} else {
|
||||
emit updated();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimatedGif::start(HistoryItem *row, const QString &file) {
|
||||
stop();
|
||||
|
||||
reader = new QImageReader(file);
|
||||
if (!reader->canRead() || !reader->supportsAnimation()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
QSize s = reader->size();
|
||||
w = s.width();
|
||||
h = s.height();
|
||||
framesCount = reader->imageCount();
|
||||
if (!w || !h || !framesCount) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
frames.reserve(framesCount);
|
||||
images.reserve(framesCount);
|
||||
delays.reserve(framesCount);
|
||||
|
||||
int32 sizeLeft = MediaViewImageSizeLimit, delay = 0;
|
||||
for (bool read = reader->read(&img); read; read = reader->read(&img)) {
|
||||
sizeLeft -= w * h * 4;
|
||||
if (img.size() != s) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
images.push_back(img);
|
||||
frames.push_back(QPixmap());
|
||||
int32 d = reader->nextImageDelay();
|
||||
if (!d) d = 1;
|
||||
delay += d;
|
||||
delays.push_back(delay);
|
||||
if (sizeLeft < 0) break;
|
||||
}
|
||||
|
||||
msg = row;
|
||||
|
||||
anim::start(this);
|
||||
if (msg) {
|
||||
msg->initDimensions();
|
||||
if (App::main()) App::main()->itemResized(msg, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedGif::stop(bool onItemRemoved) {
|
||||
if (isNull()) return;
|
||||
|
||||
delete reader;
|
||||
reader = 0;
|
||||
HistoryItem *row = msg;
|
||||
msg = 0;
|
||||
frames.clear();
|
||||
images.clear();
|
||||
delays.clear();
|
||||
w = h = frame = framesCount = duration = 0;
|
||||
|
||||
anim::stop(this);
|
||||
if (row && !onItemRemoved) {
|
||||
row->initDimensions();
|
||||
if (App::main()) App::main()->itemResized(row, true);
|
||||
}
|
||||
}
|
||||
|
||||
const QPixmap &AnimatedGif::current(int32 width, int32 height, bool rounded) {
|
||||
if (!width) width = w;
|
||||
if (!height) height = h;
|
||||
if ((frames[frame].isNull() || frames[frame].width() != width || frames[frame].height() != height) && !images[frame].isNull()) {
|
||||
QImage img = images[frame];
|
||||
if (img.width() != width || img.height() != height) img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
if (rounded) imageRound(img);
|
||||
frames[frame] = QPixmap::fromImage(img, Qt::ColorOnly);
|
||||
frames[frame].setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
return frames[frame];
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ namespace anim {
|
||||
};
|
||||
|
||||
void start(Animated *obj);
|
||||
void step(Animated *obj);
|
||||
void stop(Animated *obj);
|
||||
|
||||
void startManager();
|
||||
@@ -222,6 +223,63 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class AnimationFunc {
|
||||
public:
|
||||
virtual bool animStep(float64 ms) = 0;
|
||||
virtual ~AnimationFunc() {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class AnimationFuncOwned : public AnimationFunc {
|
||||
public:
|
||||
typedef bool (Type::*Method)(float64);
|
||||
|
||||
AnimationFuncOwned(Type *obj, Method method) : _obj(obj), _method(method) {
|
||||
}
|
||||
|
||||
bool animStep(float64 ms) {
|
||||
return (_obj->*_method)(ms);
|
||||
}
|
||||
|
||||
private:
|
||||
Type *_obj;
|
||||
Method _method;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
AnimationFunc *animFunc(Type *obj, typename AnimationFuncOwned<Type>::Method method) {
|
||||
return new AnimationFuncOwned<Type>(obj, method);
|
||||
}
|
||||
|
||||
class Animation : public Animated {
|
||||
public:
|
||||
|
||||
Animation(AnimationFunc *func) : _func(func) {
|
||||
}
|
||||
|
||||
void start() {
|
||||
anim::start(this);
|
||||
}
|
||||
void stop() {
|
||||
anim::stop(this);
|
||||
}
|
||||
|
||||
//Animation
|
||||
bool animStep(float64 ms) {
|
||||
return _func->animStep(ms);
|
||||
}
|
||||
|
||||
~Animation() {
|
||||
delete _func;
|
||||
}
|
||||
|
||||
private:
|
||||
AnimationFunc *_func;
|
||||
|
||||
};
|
||||
|
||||
class AnimationManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -248,6 +306,23 @@ public:
|
||||
obj->animInProcess = true;
|
||||
}
|
||||
|
||||
void step(Animated *obj) {
|
||||
if (iterating) return;
|
||||
|
||||
float64 ms = float64(getms());
|
||||
AnimObjs::iterator i = objs.find(obj);
|
||||
if (i != objs.cend()) {
|
||||
Animated *obj = *i;
|
||||
if (!obj->animStep(ms - obj->animStarted)) {
|
||||
objs.erase(i);
|
||||
if (!objs.size()) {
|
||||
timer.stop();
|
||||
}
|
||||
obj->animInProcess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop(Animated *obj) {
|
||||
if (iterating) {
|
||||
toStop.insert(obj);
|
||||
@@ -307,3 +382,46 @@ private:
|
||||
bool iterating;
|
||||
|
||||
};
|
||||
|
||||
class HistoryItem;
|
||||
class AnimatedGif : public QObject, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AnimatedGif() : msg(0), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) {
|
||||
}
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
void start(HistoryItem *row, const QString &file);
|
||||
void stop(bool onItemRemoved = false);
|
||||
|
||||
bool isNull() const {
|
||||
return !reader;
|
||||
}
|
||||
|
||||
~AnimatedGif() {
|
||||
stop(true);
|
||||
}
|
||||
|
||||
const QPixmap ¤t(int32 width = 0, int32 height = 0, bool rounded = false);
|
||||
|
||||
signals:
|
||||
|
||||
void updated();
|
||||
|
||||
public:
|
||||
|
||||
HistoryItem *msg;
|
||||
QImage img;
|
||||
QImageReader *reader;
|
||||
int32 w, h, frame;
|
||||
|
||||
private:
|
||||
|
||||
QVector<QPixmap> frames;
|
||||
QVector<QImage> images;
|
||||
QVector<int64> delays;
|
||||
int32 framesCount, duration;
|
||||
};
|
||||
|
||||
@@ -19,12 +19,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "boxshadow.h"
|
||||
|
||||
BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntRetinaFactor()) {
|
||||
QImage cornersImage(_size * 2, _size * 2, QImage::Format_ARGB32_Premultiplied);
|
||||
BoxShadow::BoxShadow(const style::sprite &topLeft) : _size(topLeft.pxWidth()), _pixsize(_size * cIntRetinaFactor()) {
|
||||
if (!_size) return;
|
||||
|
||||
QImage cornersImage(_pixsize * 2, _pixsize * 2, QImage::Format_ARGB32_Premultiplied);
|
||||
cornersImage.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&cornersImage);
|
||||
p.drawPixmap(QPoint(0, 0), App::sprite(), topLeft);
|
||||
p.drawPixmap(QPoint(rtl() ? _size : 0, 0), App::sprite(), topLeft);
|
||||
}
|
||||
if (rtl()) cornersImage = cornersImage.mirrored(true, false);
|
||||
uchar *bits = cornersImage.bits();
|
||||
if (bits) {
|
||||
for (
|
||||
@@ -38,37 +42,65 @@ BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntR
|
||||
{
|
||||
QPainter p(&cornersImage);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.drawImage(0, _size, cornersImage.mirrored(), 0, _size, _size, _size);
|
||||
QImage m = cornersImage.mirrored();
|
||||
m.setDevicePixelRatio(cRetinaFactor());
|
||||
p.drawImage(0, _size, m, 0, _pixsize, _pixsize, _pixsize);
|
||||
}
|
||||
{
|
||||
QPainter p(&cornersImage);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.drawImage(_size, 0, cornersImage.mirrored(true, false), _size, 0, _size, _size * 2);
|
||||
QImage m = cornersImage.mirrored(true, false);
|
||||
m.setDevicePixelRatio(cRetinaFactor());
|
||||
p.drawImage(_size, 0, m, _pixsize, 0, _pixsize, _pixsize * 2);
|
||||
}
|
||||
_corners = QPixmap::fromImage(cornersImage, Qt::ColorOnly);
|
||||
_colors.reserve(_size);
|
||||
_corners.setDevicePixelRatio(cRetinaFactor());
|
||||
_colors.reserve(_pixsize);
|
||||
uchar prev = 0;
|
||||
for (int32 i = 0; i < _size; ++i) {
|
||||
uchar a = (cornersImage.pixel(QPoint(i, _size - 1)) >> 24);
|
||||
for (int32 i = 0; i < _pixsize; ++i) {
|
||||
uchar a = (cornersImage.pixel(QPoint(i, _pixsize - 1)) >> 24);
|
||||
if (a < prev) break;
|
||||
|
||||
_colors.push_back(style::color(0, 0, 0, a));
|
||||
prev = a;
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadow::paint(QPainter &p, const QRect &box, const QPoint &shift, int32 flags) {
|
||||
int32 count = _colors.size(), minus = _size - count + 1;
|
||||
bool left = (flags & Left), top = (flags & Top), right = (flags & Right), bottom = (flags & Bottom);
|
||||
if (left && top) p.drawPixmap(box.left() - _size + minus + shift.x(), box.top() - _size + minus + shift.y(), _corners, 0, 0, _size, _size);
|
||||
if (right && top) p.drawPixmap(box.right() - minus + 1 + shift.x(), box.top() - _size + minus + shift.y(), _corners, _size, 0, _size, _size);
|
||||
if (right && bottom) p.drawPixmap(box.right() - minus + 1 + shift.x(), box.bottom() - minus + 1 + shift.y(), _corners, _size, _size, _size, _size);
|
||||
if (left && bottom) p.drawPixmap(box.left() - _size + minus + shift.x(), box.bottom() - minus + 1 + shift.y(), _corners, 0, _size, _size, _size);
|
||||
for (int32 i = 1; i <= count; ++i) {
|
||||
p.setPen(_colors[i - 1]->p);
|
||||
if (top) p.fillRect(box.left() + (left ? minus : 0) + shift.x(), box.top() - count + i + shift.y(), box.width() - (right ? minus : 0) - (left ? minus : 0), cIntRetinaFactor(), _colors[i - 1]->b);
|
||||
if (right) p.fillRect(box.right() + count - i + shift.x(), box.top() + (top ? minus : 0) + shift.y(), cIntRetinaFactor(), box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i - 1]->b);
|
||||
if (bottom) p.fillRect(box.left() + (left ? minus : 0) + shift.x(), box.bottom() + count - i + shift.y(), box.width() - (right ? minus : 0) - (left ? minus : 0), cIntRetinaFactor(), _colors[i - 1]->b);
|
||||
if (left) p.fillRect(box.left() - count + i + shift.x(), box.top() + (top ? minus : 0) + shift.y(), cIntRetinaFactor(), box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i - 1]->b);
|
||||
if (cRetina()) {
|
||||
_left = QPixmap::fromImage(cornersImage.copy(0, _pixsize - 1, _colors.size(), 1), Qt::ColorOnly);
|
||||
_left.setDevicePixelRatio(cRetinaFactor());
|
||||
_top = QPixmap::fromImage(cornersImage.copy(_pixsize - 1, 0, 1, _colors.size()), Qt::ColorOnly);
|
||||
_top.setDevicePixelRatio(cRetinaFactor());
|
||||
_right = QPixmap::fromImage(cornersImage.copy(_pixsize * 2 - _colors.size(), _pixsize, _colors.size(), 1), Qt::ColorOnly);
|
||||
_right.setDevicePixelRatio(cRetinaFactor());
|
||||
_bottom = QPixmap::fromImage(cornersImage.copy(_pixsize, _pixsize * 2 - _colors.size(), 1, _colors.size()), Qt::ColorOnly);
|
||||
_bottom.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadow::paint(QPainter &p, const QRect &box, int32 shifty, int32 flags) {
|
||||
if (!_size) return;
|
||||
|
||||
int32 rshifty = shifty * cIntRetinaFactor();
|
||||
int32 count = _colors.size(), countsize = count / cIntRetinaFactor(), minus = _size - countsize + shifty;
|
||||
bool left = (flags & Left), top = (flags & Top), right = (flags & Right), bottom = (flags & Bottom);
|
||||
if (left && top) p.drawPixmap(box.left() - _size + minus, box.top() - _size + minus + shifty, _corners, 0, 0, _pixsize, _pixsize);
|
||||
if (right && top) p.drawPixmap(box.left() + box.width() - minus, box.top() - _size + minus + shifty, _corners, _pixsize, 0, _pixsize, _pixsize);
|
||||
if (right && bottom) p.drawPixmap(box.left() + box.width() - minus, box.top() + box.height() - minus + shifty, _corners, _pixsize, _pixsize, _pixsize, _pixsize);
|
||||
if (left && bottom) p.drawPixmap(box.left() - _size + minus, box.top() + box.height() - minus + shifty, _corners, 0, _pixsize, _pixsize, _pixsize);
|
||||
if (cRetina()) {
|
||||
bool wasSmooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform);
|
||||
if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||
if (left) p.drawPixmap(box.left() - countsize + shifty, box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _left, 0, 0, count - rshifty, 1);
|
||||
if (top) p.drawPixmap(box.left() + (left ? minus : 0), box.top() - countsize + 2 * shifty, box.width() - (right ? minus : 0) - (left ? minus : 0), countsize - 2 * shifty, _top, 0, 0, 1, count - 2 * rshifty);
|
||||
if (right) p.drawPixmap(box.left() + box.width(), box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _right, rshifty, 0, count - rshifty, 1);
|
||||
if (bottom) p.drawPixmap(box.left() + (left ? minus : 0), box.top() + box.height(), box.width() - (right ? minus : 0) - (left ? minus : 0), countsize, _bottom, 0, 0, 1, count);
|
||||
if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
} else {
|
||||
p.setPen(Qt::NoPen);
|
||||
for (int32 i = 0; i < count; ++i) {
|
||||
if (left && i + shifty < count) p.fillRect(box.left() - count + i + shifty, box.top() + (top ? minus : 0) + shifty, 1, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i]->b);
|
||||
if (top && i + 2 * shifty < count) p.fillRect(box.left() + (left ? minus : 0), box.top() - count + i + 2 * shifty, box.width() - (right ? minus : 0) - (left ? minus : 0), 1, _colors[i]->b);
|
||||
if (right && i + shifty < count) p.fillRect(box.left() + box.width() + count - i - shifty - 1, box.top() + (top ? minus : 0) + shifty, 1, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i]->b);
|
||||
if (bottom) p.fillRect(box.left() + (left ? minus : 0), box.top() + box.height() + count - i - 1, box.width() - (right ? minus : 0) - (left ? minus : 0), 1, _colors[i]->b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ public:
|
||||
Bottom = 8
|
||||
};
|
||||
|
||||
BoxShadow(const style::rect &topLeft);
|
||||
BoxShadow(const style::sprite &topLeft);
|
||||
|
||||
void paint(QPainter &p, const QRect &box, const QPoint &shift = QPoint(0, 1), int32 flags = Left | Top | Right | Bottom);
|
||||
void paint(QPainter &p, const QRect &box, int32 shifty, int32 flags = Left | Top | Right | Bottom);
|
||||
|
||||
private:
|
||||
|
||||
int32 _size;
|
||||
QPixmap _corners;
|
||||
int32 _size, _pixsize;
|
||||
QPixmap _corners, _left, _top, _right, _bottom;
|
||||
QVector<style::color> _colors;
|
||||
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@ void Button::mouseReleaseEvent(QMouseEvent *e) {
|
||||
}
|
||||
|
||||
void Button::setOver(bool over, ButtonStateChangeSource source) {
|
||||
// LOG(("Set over: %1, by: %2 AT %3").arg(logBool(over)).arg(source).arg(dynamic_cast<IconedButton*>(this) ? dynamic_cast<IconedButton*>(this)->getText() : qsl("Unknown")));
|
||||
if (over && !(_state & StateOver)) {
|
||||
int oldState = _state;
|
||||
_state |= StateOver;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
#include "lang.h"
|
||||
|
||||
ContextMenu::ContextMenu(QWidget *parent, const style::iconedButton &st) : TWidget(0),
|
||||
_hiding(false), _buttonStyle(st), _shadow(st::dropdownShadow), _selected(-1), a_opacity(0), _deleteOnHide(false) {
|
||||
ContextMenu::ContextMenu(QWidget *parent, const style::dropdown &st, const style::iconedButton &btnst) : TWidget(0),
|
||||
_width(st.width), _hiding(false), _st(st), _btnst(btnst), _shadow(_st.shadow), _selected(-1), a_opacity(0), _deleteOnHide(false) {
|
||||
resetActions();
|
||||
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint | Qt::WindowStaysOnTopHint);
|
||||
@@ -43,13 +43,13 @@ QAction *ContextMenu::addAction(const QString &text, const QObject *receiver, co
|
||||
connect(a, SIGNAL(changed()), this, SLOT(actionChanged()));
|
||||
|
||||
IconedButton *b = 0;
|
||||
_buttons.push_back(b = new IconedButton(this, _buttonStyle, a->text()));
|
||||
_buttons.push_back(b = new IconedButton(this, _btnst, a->text()));
|
||||
connect(b, SIGNAL(clicked()), this, SLOT(hideStart()));
|
||||
connect(b, SIGNAL(clicked()), a, SIGNAL(triggered()));
|
||||
connect(b, SIGNAL(stateChanged(int,ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int,ButtonStateChangeSource)));
|
||||
|
||||
_width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + b->width()));
|
||||
for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - int(st::dropdownPadding.left() + st::dropdownPadding.right()), _buttons[i]->height());
|
||||
_width = qMax(_width, int(_st.padding.left() + _st.padding.right() + b->width()));
|
||||
for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - int(_st.padding.left() + _st.padding.right()), _buttons[i]->height());
|
||||
_height += b->height();
|
||||
|
||||
resize(_width, _height);
|
||||
@@ -64,8 +64,8 @@ ContextMenu::Actions &ContextMenu::actions() {
|
||||
void ContextMenu::actionChanged() {
|
||||
for (int32 i = 0, l = _actions.size(); i < l; ++i) {
|
||||
_buttons[i]->setText(_actions[i]->text());
|
||||
_width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + _buttons[i]->width()));
|
||||
_buttons[i]->resize(_width - int(st::dropdownPadding.left() + st::dropdownPadding.right()), _buttons[i]->height());
|
||||
_width = qMax(_width, int(_st.padding.left() + _st.padding.right() + _buttons[i]->width()));
|
||||
_buttons[i]->resize(_width - int(_st.padding.left() + _st.padding.right()), _buttons[i]->height());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ void ContextMenu::buttonStateChanged(int oldState, ButtonStateChangeSource sourc
|
||||
}
|
||||
|
||||
void ContextMenu::resetActions() {
|
||||
_width = st::dropdownPadding.left() + st::dropdownPadding.right();
|
||||
_height = st::dropdownPadding.top() + st::dropdownPadding.bottom();
|
||||
_width = qMax(_st.padding.left() + _st.padding.right(), int(_st.width));
|
||||
_height = _st.padding.top() + _st.padding.bottom();
|
||||
resize(_width, _height);
|
||||
|
||||
clearActions();
|
||||
@@ -122,9 +122,9 @@ void ContextMenu::clearActions() {
|
||||
}
|
||||
|
||||
void ContextMenu::resizeEvent(QResizeEvent *e) {
|
||||
int32 top = st::dropdownPadding.top();
|
||||
int32 top = _st.padding.top();
|
||||
for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) {
|
||||
(*i)->move(st::dropdownPadding.left(), top);
|
||||
(*i)->move(_st.padding.left(), top);
|
||||
top += (*i)->height();
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,7 @@ void ContextMenu::resizeEvent(QResizeEvent *e) {
|
||||
void ContextMenu::paintEvent(QPaintEvent *e) {
|
||||
QPainter p(this);
|
||||
|
||||
p.setClipRect(e->rect());
|
||||
QPainter::CompositionMode m = p.compositionMode();
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(e->rect(), st::transparent->b);
|
||||
@@ -141,9 +142,9 @@ void ContextMenu::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(a_opacity.current());
|
||||
}
|
||||
|
||||
QRect r(st::dropdownPadding.left(), st::dropdownPadding.top(), _width - st::dropdownPadding.left() - st::dropdownPadding.right(), _height - st::dropdownPadding.top() - st::dropdownPadding.bottom());
|
||||
QRect r(_st.padding.left(), _st.padding.top(), _width - _st.padding.left() - _st.padding.right(), _height - _st.padding.top() - _st.padding.bottom());
|
||||
// draw shadow
|
||||
_shadow.paint(p, r);
|
||||
_shadow.paint(p, r, _st.shadowShift);
|
||||
}
|
||||
|
||||
void ContextMenu::keyPressEvent(QKeyEvent *e) {
|
||||
@@ -245,13 +246,19 @@ void ContextMenu::deleteOnHide() {
|
||||
}
|
||||
|
||||
void ContextMenu::popup(const QPoint &p) {
|
||||
QPoint w = p - QPoint(st::dropdownPadding.left(), st::dropdownPadding.top());
|
||||
QPoint w = p - QPoint(_st.padding.left(), _st.padding.top());
|
||||
QRect r = App::app() ? App::app()->desktop()->screenGeometry(p) : QDesktopWidget().screenGeometry(p);
|
||||
if (w.x() + width() - st::dropdownPadding.right() > r.x() + r.width()) {
|
||||
w.setX(r.x() + r.width() - width() + st::dropdownPadding.right());
|
||||
if (rtl()) {
|
||||
if (w.x() - width() + 2 * _st.padding.left() < r.x() - _st.padding.left()) {
|
||||
w.setX(r.x() - _st.padding.left());
|
||||
} else {
|
||||
w.setX(w.x() - width() + 2 * _st.padding.left());
|
||||
}
|
||||
} else if (w.x() + width() - _st.padding.right() > r.x() + r.width()) {
|
||||
w.setX(r.x() + r.width() - width() + _st.padding.right());
|
||||
}
|
||||
if (w.y() + height() - st::dropdownPadding.bottom() > r.y() + r.height()) {
|
||||
w.setY(p.y() - height() + st::dropdownPadding.bottom());
|
||||
if (w.y() + height() - _st.padding.bottom() > r.y() + r.height()) {
|
||||
w.setY(p.y() - height() + _st.padding.bottom());
|
||||
}
|
||||
if (w.y() < r.y()) {
|
||||
w.setY(r.y());
|
||||
|
||||
@@ -25,7 +25,7 @@ class ContextMenu : public TWidget, public Animated {
|
||||
|
||||
public:
|
||||
|
||||
ContextMenu(QWidget *parent, const style::iconedButton &st = st::btnContext);
|
||||
ContextMenu(QWidget *parent, const style::dropdown &st = st::dropdownDef, const style::iconedButton &btnst = st::btnContext);
|
||||
QAction *addAction(const QString &text, const QObject *receiver, const char* member);
|
||||
void resetActions();
|
||||
|
||||
@@ -72,7 +72,8 @@ private:
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
|
||||
const style::iconedButton &_buttonStyle;
|
||||
const style::dropdown &_st;
|
||||
const style::iconedButton &_btnst;
|
||||
|
||||
BoxShadow _shadow;
|
||||
int32 _selected;
|
||||
|
||||
@@ -485,7 +485,7 @@ void CountrySelect::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(1);
|
||||
|
||||
QRect inner(_innerLeft, _innerTop, _innerWidth, _innerHeight);
|
||||
_shadow.paint(p, inner);
|
||||
_shadow.paint(p, inner, st::boxShadowShift);
|
||||
if (trivial || e->rect().intersects(inner)) {
|
||||
// fill bg
|
||||
p.fillRect(inner, st::white->b);
|
||||
|
||||
@@ -19,10 +19,116 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "gui/text.h"
|
||||
|
||||
void initEmoji();
|
||||
EmojiPtr getEmoji(uint32 code);
|
||||
void emojiInit();
|
||||
EmojiPtr emojiGet(uint32 code);
|
||||
EmojiPtr emojiGet(uint32 code, uint32 code2);
|
||||
EmojiPtr emojiGet(EmojiPtr emoji, uint32 color);
|
||||
EmojiPtr emojiGet(const QChar *from, const QChar *end);
|
||||
QString emojiGetSequence(int index);
|
||||
|
||||
void findEmoji(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint32 &emojiCode);
|
||||
inline QString emojiString(EmojiPtr emoji) {
|
||||
if ((emoji->code & 0xFFFF0000U) == 0xFFFF0000U) { // sequence
|
||||
return emojiGetSequence(emoji->code & 0xFFFFU);
|
||||
}
|
||||
|
||||
QString result;
|
||||
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
|
||||
if (!(emoji->code >> 16)) {
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
} else {
|
||||
result.append(QChar((emoji->code >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
if (emoji->code2) {
|
||||
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code2 & 0xFFFF));
|
||||
}
|
||||
}
|
||||
if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
|
||||
result.append(QChar((emoji->color >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->color & 0xFFFF));
|
||||
}
|
||||
if (emoji->postfix) result.append(QChar(emoji->postfix));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint64 emojiKey(EmojiPtr emoji) {
|
||||
uint64 key = emoji->code;
|
||||
if (emoji->code2) {
|
||||
key = (key << 32) | uint64(emoji->code2);
|
||||
} else if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
|
||||
key = (key << 32) | uint64(emoji->color);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
inline EmojiPtr emojiFromKey(uint64 key) {
|
||||
uint32 code = uint32(key >> 32), code2 = uint32(key & 0xFFFFFFFFLLU);
|
||||
if (!code && code2) {
|
||||
code = code2;
|
||||
code2 = 0;
|
||||
}
|
||||
EmojiPtr emoji = emojiGet(code);
|
||||
if (emoji == TwoSymbolEmoji) {
|
||||
return emojiGet(code, code2);
|
||||
} else if (emoji && emoji->color && code2) {
|
||||
return emojiGet(emoji, code2);
|
||||
}
|
||||
return emoji;
|
||||
}
|
||||
|
||||
inline EmojiPtr emojiFromUrl(const QString &url) {
|
||||
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
|
||||
}
|
||||
|
||||
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
|
||||
EmojiPtr emoji = 0;
|
||||
if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3))) {
|
||||
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
|
||||
emoji = emojiGet(code);
|
||||
if (emoji) {
|
||||
if (emoji == TwoSymbolEmoji) { // check two symbol
|
||||
if (ch + 3 >= e) {
|
||||
emoji = 0;
|
||||
} else {
|
||||
uint32 code2 = ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode()));
|
||||
emoji = emojiGet(code, code2);
|
||||
}
|
||||
} else {
|
||||
if (ch + 2 < e && (ch + 2)->unicode() == 0x200D) { // check sequence
|
||||
EmojiPtr seq = emojiGet(ch, e);
|
||||
if (seq) {
|
||||
emoji = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ch < e) {
|
||||
emoji = emojiGet(ch->unicode());
|
||||
Q_ASSERT(emoji != TwoSymbolEmoji);
|
||||
}
|
||||
|
||||
if (emoji) {
|
||||
len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
|
||||
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
|
||||
EmojiPtr col = emojiGet(emoji, color);
|
||||
if (col && col != emoji) {
|
||||
len += col->len - emoji->len;
|
||||
emoji = col;
|
||||
if (ch + len < e && (ch + len)->unicode() == 0xFE0F) {
|
||||
++len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emoji;
|
||||
}
|
||||
|
||||
extern int EmojiSizes[5], EIndex, ESize;
|
||||
extern const char *EmojiNames[5], *EName;
|
||||
|
||||
void emojiFind(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint32 &emojiCode);
|
||||
|
||||
inline bool emojiEdge(const QChar *ch) {
|
||||
return true;
|
||||
@@ -48,13 +154,14 @@ inline QString replaceEmojis(const QString &text) {
|
||||
uint32 emojiCode = 0;
|
||||
const QChar *newEmojiEnd = 0;
|
||||
if (canFindEmoji) {
|
||||
findEmoji(ch, e, newEmojiEnd, emojiCode);
|
||||
emojiFind(ch, e, newEmojiEnd, emojiCode);
|
||||
}
|
||||
|
||||
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
|
||||
++currentLink;
|
||||
}
|
||||
if (emojiCode &&
|
||||
EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0;
|
||||
if (emoji && emoji != TwoSymbolEmoji &&
|
||||
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
|
||||
(newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
|
||||
(currentLink >= lnkCount || (ch < lnkRanges[currentLink].from && newEmojiEnd <= lnkRanges[currentLink].from) || (ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len && newEmojiEnd > lnkRanges[currentLink].from + lnkRanges[currentLink].len))
|
||||
@@ -64,10 +171,18 @@ inline QString replaceEmojis(const QString &text) {
|
||||
if (ch > emojiEnd + (consumePrevious ? 1 : 0)) {
|
||||
result.append(emojiEnd, ch - emojiEnd - (consumePrevious ? 1 : 0));
|
||||
}
|
||||
if (emojiCode > 65535) {
|
||||
result.append(QChar((emojiCode >> 16) & 0xFFFF));
|
||||
if (emoji->color) {
|
||||
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
|
||||
if (it != cEmojiVariants().cend()) {
|
||||
EmojiPtr replace = emojiFromKey(it.value());
|
||||
if (replace) {
|
||||
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
|
||||
emoji = replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.append(QChar(emojiCode & 0xFFFF));
|
||||
result.append(emojiString(emoji));
|
||||
|
||||
ch = emojiEnd = newEmojiEnd;
|
||||
canFindEmoji = true;
|
||||
@@ -91,4 +206,5 @@ inline QString replaceEmojis(const QString &text) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int emojiPackCount(DBIEmojiTab tab);
|
||||
EmojiPack emojiPack(DBIEmojiTab tab);
|
||||
|
||||
@@ -172,16 +172,26 @@ void IconedButton::setText(const QString &text) {
|
||||
}
|
||||
}
|
||||
|
||||
QString IconedButton::getText() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
bool IconedButton::animStep(float64 ms) {
|
||||
float64 dt = ms / _st.duration;
|
||||
bool res = true;
|
||||
if (dt >= 1) {
|
||||
if (_st.duration <= 1) {
|
||||
a_opacity.finish();
|
||||
a_bg.finish();
|
||||
res = false;
|
||||
} else {
|
||||
a_opacity.update(dt, anim::linear);
|
||||
a_bg.update(dt, anim::linear);
|
||||
float64 dt = ms / _st.duration;
|
||||
if (dt >= 1) {
|
||||
a_opacity.finish();
|
||||
a_bg.finish();
|
||||
res = false;
|
||||
} else {
|
||||
a_opacity.update(dt, anim::linear);
|
||||
a_bg.update(dt, anim::linear);
|
||||
}
|
||||
}
|
||||
update();
|
||||
return res;
|
||||
|
||||
@@ -100,6 +100,7 @@ public:
|
||||
void setOpacity(float64 o);
|
||||
|
||||
void setText(const QString &text);
|
||||
QString getText() const;
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
@@ -174,8 +174,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
QTextCharFormat format = fragment.charFormat();
|
||||
QString imageName = static_cast<const QTextImageFormat*>(&format)->name();
|
||||
return getEmoji(imageName.mid(8).toUInt(0, 16));
|
||||
return emojiFromUrl(static_cast<const QTextImageFormat*>(&format)->name());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -288,7 +287,7 @@ void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment
|
||||
}
|
||||
if (f.isImageFormat() && !t.isEmpty() && t.at(0).unicode() == QChar::ObjectReplacementCharacter) {
|
||||
QString imageName = static_cast<QTextImageFormat*>(&f)->name();
|
||||
if (imageName.midRef(0, 8) == qsl("emoji://")) {
|
||||
if (imageName.startsWith(QLatin1String("emoji://e."))) {
|
||||
fragment = fr;
|
||||
text = t;
|
||||
return;
|
||||
@@ -372,11 +371,9 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
|
||||
case QChar::ObjectReplacementCharacter:
|
||||
if (emojiText.isEmpty() && f.isImageFormat()) {
|
||||
QString imageName = static_cast<QTextImageFormat*>(&f)->name();
|
||||
if (imageName.midRef(0, 8) == qsl("emoji://")) {
|
||||
uint32 index = imageName.mid(8).toUInt(0, 16);
|
||||
const EmojiData *emoji = getEmoji(index);
|
||||
if (emoji) {
|
||||
emojiText = textEmojiString(emoji);
|
||||
if (imageName.startsWith(QLatin1String("emoji://e."))) {
|
||||
if (EmojiPtr emoji = emojiFromUrl(imageName)) {
|
||||
emojiText = emojiString(emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,7 +517,7 @@ void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
|
||||
c.removeSelectedText();
|
||||
|
||||
QPixmap img(App::emojiSingle(emoji, _st.font->height));
|
||||
QString url = qsl("emoji://") + QString::number(emoji->code, 16);
|
||||
QString url = qsl("emoji://e.") + QString::number(emojiKey(emoji), 16);
|
||||
document()->addResource(QTextDocument::ImageResource, QUrl(url), QVariant(img));
|
||||
QTextImageFormat imageFormat;
|
||||
imageFormat.setWidth(img.width() / cIntRetinaFactor());
|
||||
@@ -546,33 +543,20 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
QTextFragment fragment(iter.fragment());
|
||||
if (!fragment.isValid()) continue;
|
||||
|
||||
int32 p = fragment.position(), e = p + fragment.length();
|
||||
if (p >= end || e <= start) {
|
||||
int32 fp = fragment.position(), fe = fp + fragment.length();
|
||||
if (fp >= end || fe <= start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString t(fragment.text());
|
||||
for (const QChar *ch = t.constData(), *e = ch + t.size(); ch != e; ++ch) {
|
||||
if (ch + 1 < e && (ch->isHighSurrogate() || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3))) {
|
||||
emoji = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
if (emoji) {
|
||||
if (emoji->len == 4 && (ch + 3 >= e || ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode())) != emoji->code2)) {
|
||||
emoji = 0;
|
||||
} else {
|
||||
emojiPosition = p + (ch - t.constData());
|
||||
emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
++ch;
|
||||
} else {
|
||||
emoji = getEmoji(ch->unicode());
|
||||
if (emoji) {
|
||||
emojiPosition = p + (ch - t.constData());
|
||||
emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
const QChar *ch = t.constData(), *e = ch + t.size();
|
||||
for (; ch != e; ++ch) {
|
||||
emoji = emojiFromText(ch, e, emojiLen);
|
||||
if (emoji) {
|
||||
emojiPosition = fp + (ch - t.constData());
|
||||
break;
|
||||
}
|
||||
if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch;
|
||||
}
|
||||
if (emoji) break;
|
||||
}
|
||||
@@ -697,8 +681,12 @@ bool FlatTextarea::animStep(float64 ms) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const QString &FlatTextarea::getLastText() const {
|
||||
return _oldtext;
|
||||
}
|
||||
|
||||
void FlatTextarea::updatePlaceholder() {
|
||||
bool vis = !hasText();
|
||||
bool vis = getLastText().isEmpty();
|
||||
if (vis == _phVisible) return;
|
||||
|
||||
a_phLeft.start(vis ? 0 : _st.phShift);
|
||||
@@ -720,10 +708,16 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const {
|
||||
|
||||
void FlatTextarea::keyPressEvent(QKeyEvent *e) {
|
||||
bool shift = e->modifiers().testFlag(Qt::ShiftModifier);
|
||||
bool macmeta = (cPlatform() == dbipMac) && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier);
|
||||
bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()) || (ctrl && shift);
|
||||
bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return);
|
||||
|
||||
if (enter && ctrlGood) {
|
||||
if (macmeta && e->key() == Qt::Key_Backspace) {
|
||||
QTextCursor tc(textCursor()), start(tc);
|
||||
start.movePosition(QTextCursor::StartOfLine);
|
||||
tc.setPosition(start.position(), QTextCursor::KeepAnchor);
|
||||
tc.removeSelectedText();
|
||||
} else if (enter && ctrlGood) {
|
||||
emit submitted(ctrl && shift);
|
||||
} else if (e->key() == Qt::Key_Escape) {
|
||||
emit cancelled();
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
|
||||
const QString &getLastText() const;
|
||||
void updatePlaceholder();
|
||||
|
||||
QRect getTextRect() const;
|
||||
@@ -85,6 +86,22 @@ signals:
|
||||
protected:
|
||||
|
||||
void insertEmoji(EmojiPtr emoji, QTextCursor c);
|
||||
TWidget *tparent() {
|
||||
return qobject_cast<TWidget*>(parentWidget());
|
||||
}
|
||||
const TWidget *tparent() const {
|
||||
return qobject_cast<const TWidget*>(parentWidget());
|
||||
}
|
||||
void enterEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->leaveToChildEvent(e);
|
||||
return QTextEdit::enterEvent(e);
|
||||
}
|
||||
void leaveEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->enterFromChildEvent(e);
|
||||
return QTextEdit::leaveEvent(e);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "gui/images.h"
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "localstorage.h"
|
||||
|
||||
namespace {
|
||||
typedef QMap<QString, LocalImage*> LocalImages;
|
||||
@@ -33,6 +34,11 @@ namespace {
|
||||
StorageImages storageImages;
|
||||
|
||||
int64 globalAquiredSize = 0;
|
||||
|
||||
static const uint64 BlurredCacheSkip = 0x1000000000000000LLU;
|
||||
static const uint64 ColoredCacheSkip = 0x2000000000000000LLU;
|
||||
static const uint64 BlurredColoredCacheSkip = 0x3000000000000000LLU;
|
||||
static const uint64 RoundedCacheSkip = 0x4000000000000000LLU;
|
||||
}
|
||||
|
||||
bool Image::isNull() const {
|
||||
@@ -43,7 +49,7 @@ ImagePtr::ImagePtr() : Parent(blank()) {
|
||||
}
|
||||
|
||||
ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def) :
|
||||
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(width, height, location.c_fileLocation().vdc_id.v, location.c_fileLocation().vvolume_id.v, location.c_fileLocation().vlocal_id.v, location.c_fileLocation().vsecret.v)) : def.v()) {
|
||||
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) {
|
||||
}
|
||||
|
||||
const QPixmap &Image::pix(int32 w, int32 h) const {
|
||||
@@ -69,6 +75,29 @@ const QPixmap &Image::pix(int32 w, int32 h) const {
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixRounded(int32 w, int32 h) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
if (w <= 0 || !width() || !height()) {
|
||||
w = width();
|
||||
} else if (cRetina()) {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = RoundedCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixNoCache(w, h, true, false, true));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
globalAquiredSize += int64(p.width()) * p.height() * 4;
|
||||
}
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
|
||||
restore();
|
||||
checkload();
|
||||
@@ -79,10 +108,10 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x1000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = BlurredCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixBlurredNoCache(w, h));
|
||||
QPixmap p(pixNoCache(w, h, true, true));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
@@ -102,7 +131,7 @@ const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) cons
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x2000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = ColoredCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixColoredNoCache(add, w, h, true));
|
||||
@@ -125,7 +154,7 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x3000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = BlurredColoredCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixBlurredColoredNoCache(add, w, h));
|
||||
@@ -138,7 +167,7 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
@@ -154,7 +183,7 @@ const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
if (i != _sizesCache.cend()) {
|
||||
globalAquiredSize -= int64(i->width()) * i->height() * 4;
|
||||
}
|
||||
QPixmap p(pixNoCache(w, h, true));
|
||||
QPixmap p(pixNoCache(w, h, true, false, true, outerw, outerh));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
@@ -164,7 +193,7 @@ const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
|
||||
const QPixmap &Image::pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
@@ -174,13 +203,13 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x1000000000000000LL | 0LL;
|
||||
uint64 k = BlurredCacheSkip | 0LL;
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) {
|
||||
if (i != _sizesCache.cend()) {
|
||||
globalAquiredSize -= int64(i->width()) * i->height() * 4;
|
||||
}
|
||||
QPixmap p(pixBlurredNoCache(w, h));
|
||||
QPixmap p(pixNoCache(w, h, true, true, true, outerw, outerh));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
@@ -308,6 +337,39 @@ yi += stride;
|
||||
return img;
|
||||
}
|
||||
|
||||
void imageRound(QImage &img) {
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
QImage **masks = App::cornersMask();
|
||||
int32 w = masks[0]->width(), h = masks[0]->height();
|
||||
int32 tw = img.width(), th = img.height();
|
||||
|
||||
uchar *bits = img.bits();
|
||||
const uchar *c0 = masks[0]->constBits(), *c1 = masks[1]->constBits(), *c2 = masks[2]->constBits(), *c3 = masks[3]->constBits();
|
||||
|
||||
int32 s0 = 0, s1 = (tw - w) * 4, s2 = (th - h) * tw * 4, s3 = ((th - h + 1) * tw - w) * 4;
|
||||
for (int32 i = 0; i < w; ++i) {
|
||||
for (int32 j = 0; j < h; ++j) {
|
||||
#define update(s, c) \
|
||||
{ \
|
||||
uint64 color = _blurGetColors(bits + s + (j * tw + i) * 4); \
|
||||
color *= (c[(j * w + i) * 4 + 3] + 1); \
|
||||
color = (color >> 8); \
|
||||
bits[s + (j * tw + i) * 4] = color & 0xFF; \
|
||||
bits[s + (j * tw + i) * 4 + 1] = (color >> 16) & 0xFF; \
|
||||
bits[s + (j * tw + i) * 4 + 2] = (color >> 32) & 0xFF; \
|
||||
bits[s + (j * tw + i) * 4 + 3] = (color >> 48) & 0xFF; \
|
||||
}
|
||||
update(s0, c0);
|
||||
update(s1, c1);
|
||||
update(s2, c2);
|
||||
update(s3, c3);
|
||||
#undef update
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QImage imageColored(const style::color &add, QImage img) {
|
||||
QImage::Format fmt = img.format();
|
||||
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
|
||||
@@ -329,35 +391,39 @@ QImage imageColored(const style::color &add, QImage img) {
|
||||
return img;
|
||||
}
|
||||
|
||||
QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth) const {
|
||||
restore();
|
||||
loaded();
|
||||
|
||||
const QPixmap &p(pixData());
|
||||
if (p.isNull()) {
|
||||
return blank()->pix();
|
||||
}
|
||||
if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return p;
|
||||
if (h <= 0) {
|
||||
return QPixmap::fromImage(p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly);
|
||||
}
|
||||
return QPixmap::fromImage(p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly);
|
||||
}
|
||||
|
||||
QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const {
|
||||
QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const {
|
||||
restore();
|
||||
loaded();
|
||||
|
||||
const QPixmap &p(pixData());
|
||||
if (p.isNull()) return blank()->pix();
|
||||
|
||||
QImage img = imageBlur(p.toImage());
|
||||
if (h <= 0) {
|
||||
img = img.scaledToWidth(w, Qt::SmoothTransformation);
|
||||
QImage img = p.toImage();
|
||||
if (blurred) img = imageBlur(img);
|
||||
if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) {
|
||||
} else if (h <= 0) {
|
||||
img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation);
|
||||
} else {
|
||||
img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation);
|
||||
}
|
||||
|
||||
if (outerw > 0 && outerh > 0) {
|
||||
outerw *= cIntRetinaFactor();
|
||||
outerh *= cIntRetinaFactor();
|
||||
if (outerw != w || outerh != h) {
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&result);
|
||||
if (w < outerw || h < outerh) {
|
||||
p.fillRect(0, 0, result.width(), result.height(), st::black->b);
|
||||
}
|
||||
p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img);
|
||||
}
|
||||
img = result;
|
||||
}
|
||||
}
|
||||
if (rounded) imageRound(img);
|
||||
return QPixmap::fromImage(img, Qt::ColorOnly);
|
||||
}
|
||||
|
||||
@@ -517,11 +583,14 @@ int64 imageCacheSize() {
|
||||
return globalAquiredSize;
|
||||
}
|
||||
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret, size)) {
|
||||
StorageImage::StorageImage(const StorageImageLocation &location, int32 size) : w(location.width), h(location.height), loader(new mtpFileLoader(location.dc, location.volume, location.local, location.secret, size)) {
|
||||
}
|
||||
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes) : w(width), h(height), loader(0) {
|
||||
StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes) : w(location.width), h(location.height), loader(0) {
|
||||
setData(bytes);
|
||||
if (location.dc) {
|
||||
Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
|
||||
}
|
||||
}
|
||||
|
||||
const QPixmap &StorageImage::pixData() const {
|
||||
@@ -608,24 +677,27 @@ bool StorageImage::loaded() const {
|
||||
return check();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) {
|
||||
StorageKey key(storageKey(dc, volume, local));
|
||||
StorageImage *getImage(const StorageImageLocation &location, int32 size) {
|
||||
StorageKey key(storageKey(location.dc, location.volume, location.local));
|
||||
StorageImages::const_iterator i = storageImages.constFind(key);
|
||||
if (i == storageImages.cend()) {
|
||||
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, size));
|
||||
i = storageImages.insert(key, new StorageImage(location, size));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) {
|
||||
StorageKey key(storageKey(dc, volume, local));
|
||||
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes) {
|
||||
StorageKey key(storageKey(location.dc, location.volume, location.local));
|
||||
StorageImages::const_iterator i = storageImages.constFind(key);
|
||||
if (i == storageImages.cend()) {
|
||||
QByteArray bytesArr(bytes);
|
||||
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, bytesArr));
|
||||
i = storageImages.insert(key, new StorageImage(location, bytesArr));
|
||||
} else if (!i.value()->loaded()) {
|
||||
QByteArray bytesArr(bytes);
|
||||
i.value()->setData(bytesArr);
|
||||
if (location.dc) {
|
||||
Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
|
||||
}
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,21 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include <QtGui/QPixmap>
|
||||
|
||||
QImage imageBlur(QImage img);
|
||||
void imageRound(QImage &img);
|
||||
|
||||
struct StorageImageLocation {
|
||||
StorageImageLocation() : width(0), height(0), dc(0), volume(0), local(0), secret(0) {
|
||||
}
|
||||
StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : width(width), height(height), dc(dc), volume(volume), local(local), secret(secret) {
|
||||
}
|
||||
StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : width(width), height(height), dc(location.vdc_id.v), volume(location.vvolume_id.v), local(location.vlocal_id.v), secret(location.vsecret.v) {
|
||||
}
|
||||
int32 width, height;
|
||||
int32 dc;
|
||||
uint64 volume;
|
||||
int32 local;
|
||||
uint64 secret;
|
||||
};
|
||||
|
||||
class Image {
|
||||
public:
|
||||
@@ -33,13 +48,13 @@ public:
|
||||
return false;
|
||||
}
|
||||
const QPixmap &pix(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixRounded(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixSingle(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurredSingle(int32 w = 0, int32 h = 0) const;
|
||||
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const;
|
||||
QPixmap pixBlurredNoCache(int32 w, int32 h = 0) const;
|
||||
const QPixmap &pixSingle(int32 w, int32 y, int32 outerw, int32 outerh) const;
|
||||
const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const;
|
||||
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false, bool blurred = false, bool rounded = false, int32 outerw = -1, int32 outerh = -1) const;
|
||||
QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const;
|
||||
QPixmap pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h = 0) const;
|
||||
|
||||
@@ -123,25 +138,66 @@ typedef QPair<uint64, uint64> StorageKey;
|
||||
inline uint64 storageMix32To64(int32 a, int32 b) {
|
||||
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
|
||||
}
|
||||
inline StorageKey storageKey(int32 dc, const int64 &volume, int32 local) {
|
||||
inline StorageKey storageKey(int32 dc, const uint64 &volume, int32 local) {
|
||||
return StorageKey(storageMix32To64(dc, local), volume);
|
||||
}
|
||||
inline StorageKey storageKey(const MTPDfileLocation &location) {
|
||||
return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v);
|
||||
}
|
||||
enum StorageFileType {
|
||||
StorageFileUnknown = 0xaa963b05, // mtpc_storage_fileUnknown
|
||||
StorageFileJpeg = 0x7efe0e, // mtpc_storage_fileJpeg
|
||||
StorageFileGif = 0xcae1aadf, // mtpc_storage_fileGif
|
||||
StorageFilePng = 0xa4f63c0, // mtpc_storage_filePng
|
||||
StorageFilePdf = 0xae1e508d, // mtpc_storage_filePdf
|
||||
StorageFileMp3 = 0x528a0677, // mtpc_storage_fileMp3
|
||||
StorageFileMov = 0x4b09ebbc, // mtpc_storage_fileMov
|
||||
StorageFilePartial = 0x40bc6f52, // mtpc_storage_filePartial
|
||||
StorageFileMp4 = 0xb3cea0e4, // mtpc_storage_fileMp4
|
||||
StorageFileWebp = 0x1081464c, // mtpc_storage_fileWebp
|
||||
};
|
||||
inline StorageFileType mtpToStorageType(mtpTypeId type) {
|
||||
switch (type) {
|
||||
case mtpc_storage_fileJpeg: return StorageFileJpeg;
|
||||
case mtpc_storage_fileGif: return StorageFileGif;
|
||||
case mtpc_storage_filePng: return StorageFilePng;
|
||||
case mtpc_storage_filePdf: return StorageFilePdf;
|
||||
case mtpc_storage_fileMp3: return StorageFileMp3;
|
||||
case mtpc_storage_fileMov: return StorageFileMov;
|
||||
case mtpc_storage_filePartial: return StorageFilePartial;
|
||||
case mtpc_storage_fileMp4: return StorageFileMp4;
|
||||
case mtpc_storage_fileWebp: return StorageFileWebp;
|
||||
case mtpc_storage_fileUnknown:
|
||||
default: return StorageFileUnknown;
|
||||
}
|
||||
}
|
||||
inline mtpTypeId mtpFromStorageType(StorageFileType type) {
|
||||
switch (type) {
|
||||
case StorageFileGif: return mtpc_storage_fileGif;
|
||||
case StorageFilePng: return mtpc_storage_filePng;
|
||||
case StorageFilePdf: return mtpc_storage_filePdf;
|
||||
case StorageFileMp3: return mtpc_storage_fileMp3;
|
||||
case StorageFileMov: return mtpc_storage_fileMov;
|
||||
case StorageFilePartial: return mtpc_storage_filePartial;
|
||||
case StorageFileMp4: return mtpc_storage_fileMp4;
|
||||
case StorageFileWebp: return mtpc_storage_fileWebp;
|
||||
case StorageFileUnknown:
|
||||
default: return mtpc_storage_fileUnknown;
|
||||
}
|
||||
}
|
||||
struct StorageImageSaved {
|
||||
StorageImageSaved() : type(mtpc_storage_fileUnknown) {
|
||||
StorageImageSaved() : type(StorageFileUnknown) {
|
||||
}
|
||||
StorageImageSaved(mtpTypeId type, const QByteArray &data) : type(type), data(data) {
|
||||
StorageImageSaved(StorageFileType type, const QByteArray &data) : type(type), data(data) {
|
||||
}
|
||||
mtpTypeId type;
|
||||
StorageFileType type;
|
||||
QByteArray data;
|
||||
};
|
||||
class StorageImage : public Image {
|
||||
public:
|
||||
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes);
|
||||
StorageImage(const StorageImageLocation &location, int32 size = 0);
|
||||
StorageImage(const StorageImageLocation &location, QByteArray &bytes);
|
||||
|
||||
int32 width() const;
|
||||
int32 height() const;
|
||||
@@ -188,8 +244,8 @@ private:
|
||||
mutable mtpFileLoader *loader;
|
||||
};
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes);
|
||||
StorageImage *getImage(const StorageImageLocation &location, int32 size = 0);
|
||||
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes);
|
||||
Image *getImage(int32 width, int32 height, const MTPFileLocation &location);
|
||||
|
||||
class ImagePtr : public ManagedPtr<Image> {
|
||||
@@ -201,9 +257,9 @@ public:
|
||||
}
|
||||
ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(getImage(pixmap, format)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0) : Parent(getImage(width, height, dc, volume, local, secret, size)) {
|
||||
ImagePtr(const StorageImageLocation &location, int32 size = 0) : Parent(getImage(location, size)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) : Parent(getImage(width, height, dc, volume, local, secret, bytes)) {
|
||||
ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr());
|
||||
};
|
||||
@@ -213,16 +269,16 @@ void clearAllImages();
|
||||
int64 imageCacheSize();
|
||||
|
||||
struct FileLocation {
|
||||
FileLocation(mtpTypeId type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) {
|
||||
FileLocation(StorageFileType type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) {
|
||||
}
|
||||
FileLocation(mtpTypeId type, const QString &name) : type(type), name(name) {
|
||||
FileLocation(StorageFileType type, const QString &name) : type(type), name(name) {
|
||||
QFileInfo f(name);
|
||||
if (f.exists()) {
|
||||
qint64 s = f.size();
|
||||
if (s > INT_MAX) {
|
||||
this->name = QString();
|
||||
size = 0;
|
||||
type = mtpc_storage_fileUnknown;
|
||||
type = StorageFileUnknown;
|
||||
} else {
|
||||
modified = f.lastModified();
|
||||
size = qint32(s);
|
||||
@@ -230,7 +286,7 @@ struct FileLocation {
|
||||
} else {
|
||||
this->name = QString();
|
||||
size = 0;
|
||||
type = mtpc_storage_fileUnknown;
|
||||
type = StorageFileUnknown;
|
||||
}
|
||||
}
|
||||
FileLocation() : size(0) {
|
||||
@@ -245,7 +301,7 @@ struct FileLocation {
|
||||
|
||||
return (f.lastModified() == modified) && (qint32(s) == size);
|
||||
}
|
||||
mtpTypeId type;
|
||||
StorageFileType type;
|
||||
QString name;
|
||||
QDateTime modified;
|
||||
qint32 size;
|
||||
@@ -257,11 +313,34 @@ inline bool operator!=(const FileLocation &a, const FileLocation &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
enum LocationType {
|
||||
UnknownFileLocation = 0,
|
||||
DocumentFileLocation = 0x4e45abe9, // mtpc_inputDocumentFileLocation
|
||||
AudioFileLocation = 0x74dc404d, // mtpc_inputAudioFileLocation
|
||||
VideoFileLocation = 0x3d0364ec, // mtpc_inputVideoFileLocation
|
||||
};
|
||||
inline LocationType mtpToLocationType(mtpTypeId type) {
|
||||
switch (type) {
|
||||
case mtpc_inputDocumentFileLocation: return DocumentFileLocation;
|
||||
case mtpc_inputAudioFileLocation: return AudioFileLocation;
|
||||
case mtpc_inputVideoFileLocation: return VideoFileLocation;
|
||||
default: return UnknownFileLocation;
|
||||
}
|
||||
}
|
||||
inline mtpTypeId mtpFromLocationType(LocationType type) {
|
||||
switch (type) {
|
||||
case DocumentFileLocation: return mtpc_inputDocumentFileLocation;
|
||||
case AudioFileLocation: return mtpc_inputAudioFileLocation;
|
||||
case VideoFileLocation: return mtpc_inputVideoFileLocation;
|
||||
case UnknownFileLocation:
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
typedef QPair<uint64, uint64> MediaKey;
|
||||
inline uint64 mediaMix32To64(mtpTypeId a, int32 b) {
|
||||
inline uint64 mediaMix32To64(int32 a, int32 b) {
|
||||
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
|
||||
}
|
||||
inline MediaKey mediaKey(mtpTypeId type, int32 dc, const int64 &id) {
|
||||
inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id) {
|
||||
return MediaKey(mediaMix32To64(type, dc), id);
|
||||
}
|
||||
inline StorageKey mediaKey(const MTPDfileLocation &location) {
|
||||
|
||||
@@ -54,7 +54,7 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st)
|
||||
}
|
||||
|
||||
void ScrollBar::recountSize() {
|
||||
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), 0, _st->width, _area->height()) : QRect(0, _area->height() - _st->width, _area->width(), _st->width));
|
||||
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), _st->deltat, _st->width, _area->height() - _st->deltat - _st->deltab) : QRect(_st->deltat, _area->height() - _st->width, _area->width() - _st->deltat - _st->deltab, _st->width));
|
||||
}
|
||||
|
||||
void ScrollBar::updateBar(bool force) {
|
||||
@@ -65,7 +65,7 @@ void ScrollBar::updateBar(bool force) {
|
||||
_area->rangeChanged(oldMax, newMax, _vertical);
|
||||
}
|
||||
if (_vertical) {
|
||||
int sh = _area->scrollHeight(), rh = height() - 2 * _st->deltay, h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
|
||||
int sh = _area->scrollHeight(), rh = height(), h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
|
||||
if (h >= rh || !_area->scrollTopMax() || rh < _st->minHeight) {
|
||||
if (!isHidden()) hide();
|
||||
bool newTopSh = (_st->topsh < 0), newBottomSh = (_st->bottomsh < 0);
|
||||
@@ -78,9 +78,9 @@ void ScrollBar::updateBar(bool force) {
|
||||
int stm = _area->scrollTopMax(), y = stm ? int32(((rh - h) * int64(_area->scrollTop())) / stm) : 0;
|
||||
if (y > rh - h) y = rh - h;
|
||||
|
||||
newBar = QRect(_st->deltax, y + _st->deltay, width() - 2 * _st->deltax, h);
|
||||
newBar = QRect(_st->deltax, y, width() - 2 * _st->deltax, h);
|
||||
} else {
|
||||
int sw = _area->scrollWidth(), rw = width() - 2 * _st->deltay, w = sw ? int32((rw * int64(_area->width())) / sw) : 0;
|
||||
int sw = _area->scrollWidth(), rw = width(), w = sw ? int32((rw * int64(_area->width())) / sw) : 0;
|
||||
if (w >= rw || !_area->scrollLeftMax() || rw < _st->minHeight) {
|
||||
if (!isHidden()) hide();
|
||||
return;
|
||||
@@ -90,11 +90,11 @@ void ScrollBar::updateBar(bool force) {
|
||||
int slm = _area->scrollLeftMax(), x = slm ? int32(((rw - w) * int64(_area->scrollLeft())) / slm) : 0;
|
||||
if (x > rw - w) x = rw - w;
|
||||
|
||||
newBar = QRect(x + _st->deltay, _st->deltax, w, height() - 2 * _st->deltax);
|
||||
newBar = QRect(x, _st->deltax, w, height() - 2 * _st->deltax);
|
||||
}
|
||||
if (newBar != _bar) {
|
||||
_bar = newBar;
|
||||
update();
|
||||
update();// parentWidget()->update(geometry());
|
||||
}
|
||||
if (_vertical) {
|
||||
bool newTopSh = (_st->topsh < 0) || (_area->scrollTop() > _st->topsh), newBottomSh = (_st->bottomsh < 0) || (_area->scrollTop() < _area->scrollTopMax() - _st->bottomsh);
|
||||
@@ -119,15 +119,16 @@ void ScrollBar::paintEvent(QPaintEvent *e) {
|
||||
if (!a_bg.current().alpha() && !a_bar.current().alpha()) return;
|
||||
QPainter p(this);
|
||||
|
||||
int32 deltax = _vertical ? _st->deltax : _st->deltay, deltay = _vertical ? _st->deltay : _st->deltax;
|
||||
int32 deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0;
|
||||
int32 deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax;
|
||||
p.setPen(Qt::NoPen);
|
||||
if (_st->round) {
|
||||
p.setBrush(a_bg.current());
|
||||
p.drawRoundedRect(QRect(deltax, deltay, width() - 2 * deltax, height() - 2 * deltay), _st->round, _st->round);
|
||||
p.drawRoundedRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), _st->round, _st->round);
|
||||
p.setBrush(a_bar.current());
|
||||
p.drawRoundedRect(_bar, _st->round, _st->round);
|
||||
} else {
|
||||
p.fillRect(QRect(deltax, deltay, width() - 2 * deltax, height() - 2 * deltay), a_bg.current());
|
||||
p.fillRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), a_bg.current());
|
||||
p.fillRect(_bar, a_bar.current());
|
||||
}
|
||||
}
|
||||
@@ -143,7 +144,7 @@ bool ScrollBar::animStep(float64 ms) {
|
||||
a_bg.update(dt, anim::linear);
|
||||
a_bar.update(dt, anim::linear);
|
||||
}
|
||||
update();
|
||||
update();// parentWidget()->update(geometry());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -176,6 +177,8 @@ void ScrollBar::leaveEvent(QEvent *e) {
|
||||
anim::start(this);
|
||||
if (_hideIn >= 0) {
|
||||
_hideTimer.start(_hideIn);
|
||||
} else if (_st->hiding) {
|
||||
hideTimeout(_st->hiding);
|
||||
}
|
||||
}
|
||||
_over = _overbar = false;
|
||||
@@ -193,7 +196,7 @@ void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
|
||||
}
|
||||
if (_moving) {
|
||||
int delta = 0, barDelta = _vertical ? (_area->height() - _bar.height()) : (_area->width() - _bar.width());
|
||||
if (barDelta) {
|
||||
if (barDelta > 0) {
|
||||
QPoint d = (e->globalPos() - _dragStart);
|
||||
delta = int32((_vertical ? (d.y() * int64(_area->scrollTopMax())) : (d.x() * int64(_area->scrollLeftMax()))) / barDelta);
|
||||
}
|
||||
@@ -209,7 +212,10 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
|
||||
if (_overbar) {
|
||||
_startFrom = _connected->value();
|
||||
} else {
|
||||
_startFrom = _vertical ? int32((e->pos().y() * int64(_area->scrollTopMax())) / height()) : ((e->pos().x() * int64(_area->scrollLeftMax())) / width());
|
||||
int32 val = _vertical ? e->pos().y() : e->pos().x(), div = _vertical ? height() : width();
|
||||
val = (val <= _st->deltat) ? 0 : (val - _st->deltat);
|
||||
div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab);
|
||||
_startFrom = _vertical ? int32((val * int64(_area->scrollTopMax())) / div) : ((val * int64(_area->scrollLeftMax())) / div);
|
||||
_connected->setValue(_startFrom);
|
||||
if (!_overbar) {
|
||||
_overbar = true;
|
||||
@@ -253,19 +259,18 @@ void ScrollBar::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
|
||||
ScrollArea::ScrollArea(QWidget *parent, const style::flatScroll &st, bool handleTouch) : QScrollArea(parent),
|
||||
_st(st),
|
||||
_disabled(false), _st(st),
|
||||
hor(this, false, &_st), vert(this, true, &_st), topSh(this, &_st), bottomSh(this, &_st),
|
||||
_touchEnabled(handleTouch), _touchScroll(false), _touchPress(false), _touchRightButton(false),
|
||||
_touchScrollState(TouchScrollManual), _touchPrevPosValid(false), _touchWaitingAcceleration(false),
|
||||
_touchSpeedTime(0), _touchAccelerationTime(0), _touchTime(0), _widgetAcceptsTouch(false) {
|
||||
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SIGNAL(scrolled()));
|
||||
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SIGNAL(scrolled()));
|
||||
setLayoutDirection(cLangDir());
|
||||
|
||||
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onScrolled()));
|
||||
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onScrolled()));
|
||||
connect(&vert, SIGNAL(topShadowVisibility(bool)), &topSh, SLOT(changeVisibility(bool)));
|
||||
connect(&vert, SIGNAL(bottomShadowVisibility(bool)), &bottomSh, SLOT(changeVisibility(bool)));
|
||||
vert.updateBar(true);
|
||||
if (_st.hiding) {
|
||||
connect(this, SIGNAL(scrolled()), this, SLOT(onScrolled()));
|
||||
}
|
||||
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
@@ -292,19 +297,31 @@ void ScrollArea::touchDeaccelerate(int32 elapsed) {
|
||||
}
|
||||
|
||||
void ScrollArea::onScrolled() {
|
||||
bool em = false;
|
||||
int32 horValue = horizontalScrollBar()->value(), vertValue = verticalScrollBar()->value();
|
||||
if (_horValue != horValue) {
|
||||
_horValue = horValue;
|
||||
if (_st.hiding) {
|
||||
hor.hideTimeout(_st.hiding);
|
||||
if (_disabled) {
|
||||
horizontalScrollBar()->setValue(_horValue);
|
||||
} else {
|
||||
_horValue = horValue;
|
||||
if (_st.hiding) {
|
||||
hor.hideTimeout(_st.hiding);
|
||||
}
|
||||
em = true;
|
||||
}
|
||||
}
|
||||
if (_vertValue != vertValue) {
|
||||
_vertValue = vertValue;
|
||||
if (_st.hiding) {
|
||||
vert.hideTimeout(_st.hiding);
|
||||
if (_disabled) {
|
||||
verticalScrollBar()->setValue(_vertValue);
|
||||
} else {
|
||||
_vertValue = vertValue;
|
||||
if (_st.hiding) {
|
||||
vert.hideTimeout(_st.hiding);
|
||||
}
|
||||
em = true;
|
||||
}
|
||||
}
|
||||
if (em) emit scrolled();
|
||||
}
|
||||
|
||||
int ScrollArea::scrollWidth() const {
|
||||
@@ -528,6 +545,21 @@ void ScrollArea::touchScrollUpdated(const QPoint &screenPos) {
|
||||
touchUpdateSpeed();
|
||||
}
|
||||
|
||||
void ScrollArea::disableScroll(bool dis) {
|
||||
_disabled = dis;
|
||||
if (_disabled) {
|
||||
hor.hideTimeout(0);
|
||||
vert.hideTimeout(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollArea::scrollContentsBy(int dx, int dy) {
|
||||
if (_disabled) {
|
||||
return;
|
||||
}
|
||||
QScrollArea::scrollContentsBy(dx, dy);
|
||||
}
|
||||
|
||||
bool ScrollArea::touchScroll(const QPoint &delta) {
|
||||
int32 scTop = scrollTop(), scMax = scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax);
|
||||
if (scNew == scTop) return false;
|
||||
@@ -559,10 +591,14 @@ void ScrollArea::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
|
||||
void ScrollArea::enterEvent(QEvent *e) {
|
||||
if (_disabled) return;
|
||||
if (_st.hiding) {
|
||||
hor.hideTimeout(_st.hiding);
|
||||
vert.hideTimeout(_st.hiding);
|
||||
}
|
||||
TWidget *p(tparent());
|
||||
if (p) p->leaveToChildEvent(e);
|
||||
return QScrollArea::enterEvent(e);
|
||||
}
|
||||
|
||||
void ScrollArea::leaveEvent(QEvent *e) {
|
||||
@@ -570,6 +606,9 @@ void ScrollArea::leaveEvent(QEvent *e) {
|
||||
hor.hideTimeout(0);
|
||||
vert.hideTimeout(0);
|
||||
}
|
||||
TWidget *p(tparent());
|
||||
if (p) p->enterFromChildEvent(e);
|
||||
return QScrollArea::leaveEvent(e);
|
||||
}
|
||||
|
||||
void ScrollArea::scrollToY(int toTop, int toBottom) {
|
||||
|
||||
@@ -134,6 +134,7 @@ public:
|
||||
public slots:
|
||||
|
||||
void scrollToY(int toTop, int toBottom = -1);
|
||||
void disableScroll(bool dis);
|
||||
void onScrolled();
|
||||
|
||||
void onTouchTimer();
|
||||
@@ -146,6 +147,16 @@ signals:
|
||||
void scrollFinished();
|
||||
void geometryChanged();
|
||||
|
||||
protected:
|
||||
|
||||
void scrollContentsBy(int dx, int dy);
|
||||
TWidget *tparent() {
|
||||
return qobject_cast<TWidget*>(parentWidget());
|
||||
}
|
||||
const TWidget *tparent() const {
|
||||
return qobject_cast<const TWidget*>(parentWidget());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool touchScroll(const QPoint &delta);
|
||||
@@ -156,6 +167,8 @@ private:
|
||||
void touchUpdateSpeed();
|
||||
void touchDeaccelerate(int32 elapsed);
|
||||
|
||||
bool _disabled;
|
||||
|
||||
style::flatScroll _st;
|
||||
ScrollBar hor, vert;
|
||||
ScrollShadow topSh, bottomSh;
|
||||
|
||||
@@ -26,6 +26,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include <QtGui/QCursor>
|
||||
#include <QtGui/QFont>
|
||||
|
||||
inline QRect rtlrect(int x, int y, int w, int h, int outerw) {
|
||||
return rtl() ? QRect(outerw - x - w, y, w, h) : QRect(x, y, w, h);
|
||||
}
|
||||
inline QRect centerrect(const QRect &inRect, const QRect &rect) {
|
||||
return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height());
|
||||
}
|
||||
|
||||
namespace style {
|
||||
|
||||
class FontData;
|
||||
@@ -197,14 +204,17 @@ inline bool operator!=(const Font &a, const Font &b) {
|
||||
typedef QMap<uint32, ColorData*> ColorDatas;
|
||||
extern ColorDatas _colorsMap;
|
||||
|
||||
extern int _spriteWidth;
|
||||
|
||||
typedef float64 number;
|
||||
typedef QString string;
|
||||
typedef QRect rect;
|
||||
class sprite : public rect {
|
||||
|
||||
class sprite : public rect {
|
||||
public:
|
||||
sprite() {
|
||||
}
|
||||
sprite(int left, int top, int width, int height) : rect(left, top, width, height) {
|
||||
sprite(int left, int top, int width, int height) : rect(rtl() ? (_spriteWidth - left - width) : left, top, width, height) {
|
||||
}
|
||||
inline int pxWidth() const {
|
||||
return rect::width() / cIntRetinaFactor();
|
||||
@@ -260,3 +270,7 @@ inline bool operator!=(const Font &a, const Font &b) {
|
||||
void stopManager();
|
||||
|
||||
};
|
||||
|
||||
inline QRect centersprite(const QRect &inRect, const style::sprite &sprite) {
|
||||
return centerrect(inRect, QRect(QPoint(0, 0), sprite.pxSize()));
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace {
|
||||
|
||||
const QRegularExpression _reDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
||||
const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
||||
const QRegularExpression _reDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
|
||||
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
|
||||
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
@@ -239,26 +239,6 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink)
|
||||
return (result < end && *result == TextCommand) ? (result + 1) : from;
|
||||
}
|
||||
|
||||
QString textEmojiString(EmojiPtr emoji) {
|
||||
QString result;
|
||||
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
|
||||
switch (emoji->len) {
|
||||
case 1: result.append(QChar(emoji->code & 0xFFFF)); break;
|
||||
case 2:
|
||||
result.append(QChar((emoji->code >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
break;
|
||||
case 4:
|
||||
result.append(QChar((emoji->code >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code2 & 0xFFFF));
|
||||
break;
|
||||
}
|
||||
if (emoji->postfix) result.append(QChar(emoji->postfix));
|
||||
return result;
|
||||
}
|
||||
|
||||
class TextParser {
|
||||
public:
|
||||
|
||||
@@ -515,24 +495,19 @@ public:
|
||||
}
|
||||
|
||||
void parseEmojiFromCurrent() {
|
||||
const EmojiData *e = getEmoji(chInt);
|
||||
int len = 0, skipped = (chInt > 0xFFFFU) ? 1 : 0;
|
||||
EmojiPtr e = emojiFromText(ptr - skipped, end, len);
|
||||
if (!e) return;
|
||||
|
||||
if (e->len > 2) {
|
||||
if (ptr + 2 >= end || e->code2 != ((uint32((ptr + 1)->unicode()) << 16) | uint32((ptr + 2)->unicode()))) {
|
||||
return;
|
||||
} else {
|
||||
_t->_text.push_back(*++ptr);
|
||||
_t->_text.push_back(*++ptr);
|
||||
}
|
||||
}
|
||||
int emojiLen = e->len;
|
||||
if (ptr + 1 < end && (ptr + 1)->unicode() == 0xFE0F) {
|
||||
for (int l = len - skipped - 1; l > 0; --l) {
|
||||
_t->_text.push_back(*++ptr);
|
||||
++emojiLen;
|
||||
}
|
||||
if (e->postfix && _t->_text.at(_t->_text.size() - 1).unicode() != e->postfix) {
|
||||
_t->_text.push_back(e->postfix);
|
||||
++len;
|
||||
}
|
||||
|
||||
createBlock(-emojiLen);
|
||||
createBlock(-len);
|
||||
emoji = e;
|
||||
}
|
||||
|
||||
@@ -759,9 +734,15 @@ namespace {
|
||||
void TextLink::onClick(Qt::MouseButton button) const {
|
||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||
QString url = TextLink::encoded();
|
||||
QRegularExpressionMatch telegramMe = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
if (telegramMe.hasMatch()) {
|
||||
App::openUserByName(telegramMe.captured(1));
|
||||
QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
if (telegramMeUser.hasMatch()) {
|
||||
App::openUserByName(telegramMeUser.captured(1));
|
||||
} else if (telegramMeGroup.hasMatch()) {
|
||||
App::joinGroupByHash(telegramMeGroup.captured(1));
|
||||
} else if (telegramMeStickers.hasMatch()) {
|
||||
App::stickersBox(telegramMeStickers.captured(1));
|
||||
} else if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) {
|
||||
App::openLocalUrl(url);
|
||||
} else {
|
||||
@@ -887,7 +868,7 @@ public:
|
||||
_align = align;
|
||||
|
||||
_parDirection = _t->_startDir;
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir();
|
||||
if ((*_t->_blocks.cbegin())->type() != TextBlockNewline) {
|
||||
initNextParagraph(_t->_blocks.cbegin());
|
||||
}
|
||||
@@ -926,7 +907,7 @@ public:
|
||||
}
|
||||
|
||||
_parDirection = static_cast<NewlineBlock*>(b)->nextDirection();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir();
|
||||
initNextParagraph(i + 1);
|
||||
|
||||
longWordLine = true;
|
||||
@@ -1148,12 +1129,12 @@ public:
|
||||
|
||||
if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) {
|
||||
if (x > _x) {
|
||||
_p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) {
|
||||
if (x < _x + _wLeft) {
|
||||
_p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1311,19 +1292,19 @@ public:
|
||||
const QChar *chFrom = _str + currentBlock->from(), *chTo = chFrom + ((nextBlock ? nextBlock->from() : _t->_text.size()) - currentBlock->from());
|
||||
if (_localFrom + si.position >= _selectedFrom) { // could be without space
|
||||
if (chTo == chFrom || (chTo - 1)->unicode() != QChar::Space || _selectedTo >= (chTo - _str)) {
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
} else { // or with space
|
||||
_p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
} else if (chTo > chFrom && (chTo - 1)->unicode() == QChar::Space && (chTo - 1 - _str) >= _selectedFrom) {
|
||||
if (rtl) { // rtl space only
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
} else { // ltr space only
|
||||
_p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
}
|
||||
_p->drawPixmap(QPoint((glyphX + int(st::emojiPadding)).toInt(), _y + _yDelta + emojiY), App::emojis(), QRect(static_cast<EmojiBlock*>(currentBlock)->emoji->x, static_cast<EmojiBlock*>(currentBlock)->emoji->y, st::emojiImgSize, st::emojiImgSize));
|
||||
emojiDraw(*_p, static_cast<EmojiBlock*>(currentBlock)->emoji, (glyphX + int(st::emojiPadding)).toInt(), _y + _yDelta + emojiY);
|
||||
// } else if (_p && currentBlock->type() == TextBlockSkip) { // debug
|
||||
// _p->fillRect(QRect(x.toInt(), _y, currentBlock->width(), static_cast<SkipBlock*>(currentBlock)->height()), QColor(0, 0, 0, 32));
|
||||
}
|
||||
@@ -1445,7 +1426,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (rtl) selX = x + itemWidth - (selX - x) - selWidth;
|
||||
_p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
|
||||
_p->drawTextItem(QPointF(x.toReal(), textY), gf);
|
||||
@@ -2262,6 +2243,21 @@ _startDir(other._startDir)
|
||||
}
|
||||
}
|
||||
|
||||
Text &Text::operator=(const Text &other) {
|
||||
_minResizeWidth = other._minResizeWidth;
|
||||
_maxWidth = other._maxWidth;
|
||||
_minHeight = other._minHeight;
|
||||
_text = other._text;
|
||||
_font = other._font;
|
||||
_blocks = TextBlocks(other._blocks.size());
|
||||
_links = other._links;
|
||||
_startDir = other._startDir;
|
||||
for (int32 i = 0, l = _blocks.size(); i < l; ++i) {
|
||||
_blocks[i] = other._blocks.at(i)->clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Text::setText(style::font font, const QString &text, const TextParseOptions &options) {
|
||||
if (!_textStyle) _initDefault();
|
||||
_font = font;
|
||||
@@ -2613,7 +2609,7 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, bool expandLinks)
|
||||
result += r;
|
||||
} else {
|
||||
QUrl u(url);
|
||||
if (r.size() > 3 && _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link
|
||||
if (r.size() <= 3 || _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link
|
||||
result += url;
|
||||
} else {
|
||||
result.append(r).append(qsl(" ( ")).append(url).append(qsl(" )"));
|
||||
@@ -4047,29 +4043,19 @@ bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
EmojiPtr e = 0;
|
||||
if (ch->isHighSurrogate()) {
|
||||
if (ch + 1 < end && (ch + 1)->isLowSurrogate()) {
|
||||
e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
if (!e) {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ch + 1 < end) {
|
||||
if (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) {
|
||||
e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
} else if ((ch + 1)->unicode() == 0xFE0F) {
|
||||
e = getEmoji(ch->unicode());
|
||||
}
|
||||
}
|
||||
}
|
||||
int elen = 0;
|
||||
EmojiPtr e = emojiFromText(ch, end, elen);
|
||||
if (e) {
|
||||
ch += (e->len - 1);
|
||||
if (ch + 1 < end && (ch + 1)->unicode() == 0xFE0F) {
|
||||
++ch;
|
||||
++s;
|
||||
for (int i = 0; i < elen; ++i, ++ch, ++s) {
|
||||
if (ch->isHighSurrogate() && i + 1 < elen && (ch + 1)->isLowSurrogate()) {
|
||||
++ch;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
--ch;
|
||||
--s;
|
||||
} else if (ch->isHighSurrogate() && ch + 1 < end && (ch + 1)->isLowSurrogate()) {
|
||||
++ch;
|
||||
}
|
||||
if (s >= limit) {
|
||||
sendingText = leftText.mid(0, good - start);
|
||||
@@ -4090,7 +4076,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
initLinkSets();
|
||||
int32 len = text.size(), nextCmd = rich ? 0 : len;
|
||||
const QChar *start = text.unicode(), *end = start + text.size();
|
||||
for (int32 offset = 0, matchOffset = offset; offset < len;) {
|
||||
for (int32 offset = 0, matchOffset = offset, mentionSkip = 0; offset < len;) {
|
||||
if (nextCmd <= offset) {
|
||||
for (nextCmd = offset; nextCmd < len; ++nextCmd) {
|
||||
if (*(start + nextCmd) == TextCommand) {
|
||||
@@ -4101,8 +4087,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
QRegularExpressionMatch mDomain = _reDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch()) break;
|
||||
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
|
||||
|
||||
LinkRange link;
|
||||
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
|
||||
@@ -4121,7 +4106,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
--hashtagEnd;
|
||||
}
|
||||
}
|
||||
if (mMention.hasMatch()) {
|
||||
while (mMention.hasMatch()) {
|
||||
if (!mMention.capturedRef(1).isEmpty()) {
|
||||
++mentionOffset;
|
||||
}
|
||||
@@ -4129,10 +4114,21 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
--mentionEnd;
|
||||
}
|
||||
if (!(start + mentionOffset + 1)->isLetter() || !(start + mentionEnd - 1)->isLetterOrNumber()) {
|
||||
mentionOffset = mentionEnd = INT_MAX;
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
mentionSkip = mentionEnd;
|
||||
mMention = _reMention.match(text, qMax(mentionSkip, matchOffset));
|
||||
if (mMention.hasMatch()) {
|
||||
mentionOffset = mMention.capturedStart();
|
||||
mentionEnd = mMention.capturedEnd();
|
||||
} else {
|
||||
mentionOffset = INT_MAX;
|
||||
mentionEnd = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mMention.hasMatch() && !mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
|
||||
if (explicitDomainOffset < domainOffset) {
|
||||
domainOffset = explicitDomainOffset;
|
||||
domainEnd = explicitDomainEnd;
|
||||
@@ -4237,3 +4233,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
|
||||
return lnkRanges;
|
||||
}
|
||||
|
||||
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y) {
|
||||
p.drawPixmap(QPoint(x, y), App::emojis(), QRect(e->x * ESize, e->y * ESize, ESize, ESize));
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich = false);
|
||||
|
||||
#include "gui/emoji_config.h"
|
||||
|
||||
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y);
|
||||
|
||||
#include "../../../QtStatic/qtbase/src/gui/text/qfontengine_p.h"
|
||||
|
||||
enum TextBlockType {
|
||||
@@ -424,6 +426,7 @@ public:
|
||||
Text(int32 minResizeWidth = QFIXED_MAX);
|
||||
Text(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false);
|
||||
Text(const Text &other);
|
||||
Text &operator=(const Text &other);
|
||||
|
||||
int32 countHeight(int32 width) const;
|
||||
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
|
||||
@@ -432,6 +435,10 @@ public:
|
||||
void setLink(uint16 lnkIndex, const TextLinkPtr &lnk);
|
||||
bool hasLinks() const;
|
||||
|
||||
bool hasSkipBlock() const {
|
||||
return _blocks.isEmpty() ? false : _blocks.back()->type() == TextBlockSkip;
|
||||
}
|
||||
|
||||
int32 maxWidth() const {
|
||||
return _maxWidth.ceil().toInt();
|
||||
}
|
||||
@@ -532,8 +539,6 @@ QString textcmdStartColor(const style::color &color);
|
||||
QString textcmdStopColor();
|
||||
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);
|
||||
|
||||
QString textEmojiString(EmojiPtr emoji);
|
||||
|
||||
inline bool chIsSpace(QChar ch, bool rich = false) {
|
||||
return ch.isSpace() || (ch < 32 && !(rich && ch == TextCommand)) || (ch == QChar::ParagraphSeparator) || (ch == QChar::LineSeparator) || (ch == QChar::ObjectReplacementCharacter) || (ch == QChar::SoftHyphen) || (ch == QChar::CarriageReturn) || (ch == QChar::Tabulation);
|
||||
}
|
||||
@@ -544,10 +549,11 @@ inline bool chIsTrimmed(QChar ch, bool rich = false) {
|
||||
return (!rich || ch != TextCommand) && (chIsSpace(ch) || chIsBad(ch));
|
||||
}
|
||||
inline bool chIsDiac(QChar ch) { // diac and variation selectors
|
||||
return (ch >= 768 && ch < 880) || (ch >= 7616 && ch < 7680) || (ch >= 8400 && ch < 8448) || (ch >= 65056 && ch < 65072);
|
||||
QChar::Category c = ch.category();
|
||||
return (c == QChar::Mark_NonSpacing);
|
||||
}
|
||||
inline int32 chMaxDiacAfterSymbol() {
|
||||
return 4;
|
||||
return 2;
|
||||
}
|
||||
inline bool chIsNewline(QChar ch) {
|
||||
return (ch == QChar::LineFeed || ch == 156);
|
||||
|
||||
@@ -20,9 +20,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "application.h"
|
||||
|
||||
namespace {
|
||||
Qt::LayoutDirection _dir = Qt::LeftToRight;
|
||||
bool _rtl = false;
|
||||
|
||||
void _sendResizeEvents(QWidget *target) {
|
||||
QResizeEvent e(target->size(), QSize());
|
||||
QApplication::sendEvent(target, &e);
|
||||
@@ -37,19 +34,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
void rtl(bool is) {
|
||||
_rtl = is;
|
||||
_dir = _rtl ? Qt::RightToLeft : Qt::LeftToRight;
|
||||
}
|
||||
|
||||
bool rtl() {
|
||||
return _rtl;
|
||||
}
|
||||
|
||||
Qt::LayoutDirection langDir() { // current lang dependent
|
||||
return _dir;
|
||||
}
|
||||
|
||||
QPixmap myGrab(QWidget *target, const QRect &rect) {
|
||||
if (!cRetina()) return target->grab(rect);
|
||||
|
||||
|
||||
@@ -17,38 +17,83 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void rtl(bool is);
|
||||
bool rtl();
|
||||
Qt::LayoutDirection langDir();
|
||||
|
||||
class Widget : public QWidget {
|
||||
public:
|
||||
|
||||
Widget(QWidget *parent = 0) : QWidget(parent) {
|
||||
}
|
||||
void moveToLeft(int x, int y, int w) {
|
||||
move(rtl() ? (x + w - width()) : x, y);
|
||||
void moveToLeft(int x, int y, int outerw) {
|
||||
move(rtl() ? (outerw - x - width()) : x, y);
|
||||
}
|
||||
void moveToRight(int x, int y, int w) {
|
||||
move(rtl() ? x : (x + w - width()), y);
|
||||
void moveToRight(int x, int y, int outerw) {
|
||||
move(rtl() ? x : (outerw - x - width()), y);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace App {
|
||||
const QPixmap &sprite();
|
||||
}
|
||||
|
||||
class Painter : public QPainter {
|
||||
public:
|
||||
explicit Painter(QPaintDevice *device) : QPainter(device) {
|
||||
}
|
||||
|
||||
void drawTextLeft(int x, int y, int w, const QString &text, int textWidth = -1) {
|
||||
void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) {
|
||||
QFontMetrics m(fontMetrics());
|
||||
if (rtl() && textWidth < 0) textWidth = m.width(text);
|
||||
drawText(x + (rtl() ? (w - textWidth) : 0), y + m.ascent(), text);
|
||||
drawText(rtl() ? (outerw - x - textWidth) : x, y + m.ascent(), text);
|
||||
}
|
||||
void drawTextRight(int x, int y, int w, const QString &text, int textWidth = -1) {
|
||||
void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1) {
|
||||
QFontMetrics m(fontMetrics());
|
||||
if (!rtl() && textWidth < 0) textWidth = m.width(text);
|
||||
drawText(x + (rtl() ? 0 : (w - textWidth)), y + m.ascent(), text);
|
||||
drawText(rtl() ? x : (outerw - x - textWidth), y + m.ascent(), text);
|
||||
}
|
||||
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
drawPixmap(QPoint(rtl() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from);
|
||||
}
|
||||
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
return drawPixmapLeft(p.x(), p.y(), outerw, pix, from);
|
||||
}
|
||||
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix) {
|
||||
drawPixmap(QPoint(rtl() ? (outerw - x - (pix.width() / pix.devicePixelRatio())) : x, y), pix);
|
||||
}
|
||||
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix) {
|
||||
return drawPixmapLeft(p.x(), p.y(), outerw, pix);
|
||||
}
|
||||
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
drawPixmap(QPoint(rtl() ? x : (outerw - x - (from.width() / pix.devicePixelRatio())), y), pix, from);
|
||||
}
|
||||
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
return drawPixmapRight(p.x(), p.y(), outerw, pix, from);
|
||||
}
|
||||
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix) {
|
||||
drawPixmap(QPoint(rtl() ? x : (outerw - x - (pix.width() / pix.devicePixelRatio())), y), pix);
|
||||
}
|
||||
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix) {
|
||||
return drawPixmapRight(p.x(), p.y(), outerw, pix);
|
||||
}
|
||||
void drawSprite(int x, int y, const style::sprite &sprite) {
|
||||
return drawPixmap(QPoint(x, y), App::sprite(), sprite);
|
||||
}
|
||||
void drawSprite(const QPoint &p, const style::sprite &sprite) {
|
||||
return drawPixmap(p, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteLeft(int x, int y, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapLeft(x, y, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteLeft(const QPoint &p, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapLeft(p, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteRight(int x, int y, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapRight(x, y, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteRight(const QPoint &p, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapRight(p, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteCenter(const QRect &in, const style::sprite &sprite) {
|
||||
return drawPixmap(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), App::sprite(), sprite);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,10 +125,12 @@ protected:
|
||||
void enterEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->leaveToChildEvent(e);
|
||||
return Widget::enterEvent(e);
|
||||
}
|
||||
void leaveEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->enterFromChildEvent(e);
|
||||
return Widget::leaveEvent(e);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -34,7 +34,6 @@ extern TextParseOptions _textNameOptions, _textDlgOptions;
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
|
||||
struct History;
|
||||
struct Histories : public QHash<PeerId, History*>, public Animated {
|
||||
typedef QHash<PeerId, History*> Parent;
|
||||
@@ -218,6 +217,9 @@ struct History : public QList<HistoryBlock*> {
|
||||
HistoryItem *currentNotification() {
|
||||
return notifies.isEmpty() ? 0 : notifies.front();
|
||||
}
|
||||
bool hasNotification() const {
|
||||
return !notifies.isEmpty();
|
||||
}
|
||||
void skipNotification() {
|
||||
if (!notifies.isEmpty()) {
|
||||
notifies.pop_front();
|
||||
@@ -620,6 +622,8 @@ private:
|
||||
ItemAnimations &itemAnimations();
|
||||
|
||||
class HistoryReply; // dynamic_cast optimize
|
||||
class HistoryMessage; // dynamic_cast optimize
|
||||
class HistoryForwarded; // dynamic_cast optimize
|
||||
|
||||
class HistoryMedia;
|
||||
class HistoryItem : public HistoryElem {
|
||||
@@ -655,6 +659,9 @@ public:
|
||||
bool detached() const {
|
||||
return !_block;
|
||||
}
|
||||
void attach(HistoryBlock *block) {
|
||||
_block = block;
|
||||
}
|
||||
bool out() const {
|
||||
return _flags & MTPDmessage_flag_out;
|
||||
}
|
||||
@@ -665,6 +672,12 @@ public:
|
||||
bool notifyByFrom() const {
|
||||
return _flags & MTPDmessage_flag_notify_by_from;
|
||||
}
|
||||
bool isMediaUnread() const {
|
||||
return _flags & MTPDmessage_flag_media_unread;
|
||||
}
|
||||
void markMediaRead() {
|
||||
_flags &= ~MTPDmessage_flag_media_unread;
|
||||
}
|
||||
virtual bool needCheck() const {
|
||||
return true;
|
||||
}
|
||||
@@ -722,13 +735,25 @@ public:
|
||||
virtual QString time() const {
|
||||
return QString();
|
||||
}
|
||||
virtual int32 timeWidth() const {
|
||||
virtual int32 timeWidth(bool forText) const {
|
||||
return 0;
|
||||
}
|
||||
virtual bool animating() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual HistoryForwarded *toHistoryForwarded() { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual const HistoryForwarded *toHistoryForwarded() const { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual HistoryReply *toHistoryReply() { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
@@ -779,6 +804,9 @@ public:
|
||||
virtual const QString inDialogsText() const = 0;
|
||||
virtual const QString inHistoryText() const = 0;
|
||||
virtual bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
|
||||
virtual bool isDisplayed() const {
|
||||
return true;
|
||||
}
|
||||
virtual int32 countHeight(const HistoryItem *parent, int32 width = -1) const {
|
||||
return height();
|
||||
}
|
||||
@@ -802,6 +830,10 @@ public:
|
||||
virtual void updateFrom(const MTPMessageMedia &media) {
|
||||
}
|
||||
|
||||
virtual bool isImageLink() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool updateStickerEmoji() {
|
||||
return false;
|
||||
}
|
||||
@@ -830,7 +862,7 @@ protected:
|
||||
class HistoryPhoto : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryPhoto(const MTPDphoto &photo);
|
||||
HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent);
|
||||
HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
|
||||
|
||||
void init();
|
||||
@@ -843,6 +875,7 @@ public:
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
const QString inHistoryText() const;
|
||||
const Text &captionForClone() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
HistoryMedia *clone() const;
|
||||
@@ -870,19 +903,22 @@ public:
|
||||
private:
|
||||
int16 pixw, pixh;
|
||||
PhotoData *data;
|
||||
Text _caption;
|
||||
TextLinkPtr openl;
|
||||
|
||||
};
|
||||
|
||||
QString formatSizeText(qint64 size);
|
||||
QString formatDurationText(qint64 duration);
|
||||
|
||||
class HistoryVideo : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryVideo(const MTPDvideo &video);
|
||||
HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent);
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeVideo;
|
||||
}
|
||||
@@ -907,6 +943,8 @@ private:
|
||||
VideoData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
|
||||
Text _caption;
|
||||
|
||||
QString _size;
|
||||
int32 _thumbw, _thumbx, _thumby;
|
||||
|
||||
@@ -933,9 +971,15 @@ public:
|
||||
}
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
AudioData *audio() {
|
||||
return data;
|
||||
}
|
||||
|
||||
void regItem(HistoryItem *item);
|
||||
void unregItem(HistoryItem *item);
|
||||
|
||||
void updateFrom(const MTPMessageMedia &media);
|
||||
|
||||
private:
|
||||
AudioData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
@@ -1064,6 +1108,9 @@ public:
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
bool isDisplayed() const {
|
||||
return !data->pendingTill;
|
||||
}
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeWebPage;
|
||||
@@ -1151,7 +1198,7 @@ private:
|
||||
class HistoryImageLink : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryImageLink(const QString &url);
|
||||
HistoryImageLink(const QString &url, const QString &title = QString(), const QString &description = QString());
|
||||
int32 fullWidth() const;
|
||||
int32 fullHeight() const;
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
@@ -1167,8 +1214,13 @@ public:
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
bool isImageLink() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ImageLinkData *data;
|
||||
Text _title, _description;
|
||||
TextLinkPtr link;
|
||||
|
||||
};
|
||||
@@ -1181,6 +1233,7 @@ public:
|
||||
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
|
||||
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc);
|
||||
|
||||
void initTime();
|
||||
void initMedia(const MTPMessageMedia &media, QString ¤tText);
|
||||
void initMediaFromText(QString ¤tText);
|
||||
void initMediaFromDocument(DocumentData *doc);
|
||||
@@ -1199,7 +1252,10 @@ public:
|
||||
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
virtual void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
|
||||
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return _text.adjustSelection(from, to, type);
|
||||
@@ -1210,7 +1266,9 @@ public:
|
||||
QString notificationText() const;
|
||||
|
||||
void updateMedia(const MTPMessageMedia &media) {
|
||||
if (_media) _media->updateFrom(media);
|
||||
if (_media) {
|
||||
_media->updateFrom(media);
|
||||
}
|
||||
}
|
||||
void updateStickerEmoji();
|
||||
|
||||
@@ -1222,8 +1280,8 @@ public:
|
||||
QString time() const {
|
||||
return _time;
|
||||
}
|
||||
int32 timeWidth() const {
|
||||
return _timeWidth;
|
||||
int32 timeWidth(bool forText) const {
|
||||
return _timeWidth + (forText ? (st::msgDateSpace + (out() ? st::msgDateCheckSpace + st::msgCheckRect.pxWidth() : 0) - st::msgDateDelta.x()) : 0);
|
||||
}
|
||||
virtual bool animating() const {
|
||||
return _media ? _media->animating() : false;
|
||||
@@ -1236,6 +1294,13 @@ public:
|
||||
return from();
|
||||
}
|
||||
|
||||
HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
|
||||
return this;
|
||||
}
|
||||
const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize
|
||||
return this;
|
||||
}
|
||||
|
||||
~HistoryMessage();
|
||||
|
||||
protected:
|
||||
@@ -1260,10 +1325,13 @@ public:
|
||||
void fwdNameUpdated() const;
|
||||
|
||||
void draw(QPainter &p, uint32 selection) const;
|
||||
void drawForwardedFrom(QPainter &p, int32 x, int32 y, int32 w, bool selected) const;
|
||||
void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
|
||||
void getForwardedState(TextLinkPtr &lnk, bool &inText, int32 x, int32 w) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
|
||||
QDateTime dateForwarded() const {
|
||||
@@ -1274,6 +1342,13 @@ public:
|
||||
}
|
||||
QString selectedText(uint32 selection) const;
|
||||
|
||||
HistoryForwarded *toHistoryForwarded() {
|
||||
return this;
|
||||
}
|
||||
const HistoryForwarded *toHistoryForwarded() const {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
QDateTime fwdDate;
|
||||
@@ -1308,6 +1383,7 @@ public:
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
|
||||
UserData *replyTo() const {
|
||||
|
||||
@@ -250,7 +250,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class HistoryWidget : public QWidget, public RPCSender, public Animated {
|
||||
class HistoryWidget : public TWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -273,6 +273,8 @@ public:
|
||||
void leaveEvent(QEvent *e);
|
||||
void dropEvent(QDropEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
||||
void updateTopBarSelection();
|
||||
@@ -296,8 +298,8 @@ public:
|
||||
QRect historyRect() const;
|
||||
|
||||
void updateTyping(bool typing = true);
|
||||
// void updateStickerPan();
|
||||
void updateRecentStickers();
|
||||
void stickersInstalled(uint64 setId);
|
||||
void typingDone(const MTPBool &result, mtpRequestId req);
|
||||
|
||||
void destroyData();
|
||||
@@ -316,7 +318,6 @@ public:
|
||||
void updateOnlineDisplay(int32 x, int32 w);
|
||||
void updateOnlineDisplayTimer();
|
||||
|
||||
// mtpRequestId onForward(const PeerId &peer, SelectedItemSet toForward);
|
||||
void onShareContact(const PeerId &peer, UserData *contact);
|
||||
void onSendPaths(const PeerId &peer);
|
||||
|
||||
@@ -329,7 +330,7 @@ public:
|
||||
int32 lastScrollTop() const;
|
||||
|
||||
void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false);
|
||||
bool animStep(float64 ms);
|
||||
bool showStep(float64 ms);
|
||||
void animStop();
|
||||
|
||||
QPoint clampMousePosition(QPoint point);
|
||||
@@ -366,6 +367,10 @@ public:
|
||||
void updatePreview();
|
||||
void previewCancel();
|
||||
|
||||
bool recordStep(float64 ms);
|
||||
bool recordingStep(float64 ms);
|
||||
void stopRecording(bool send);
|
||||
|
||||
~HistoryWidget();
|
||||
|
||||
signals:
|
||||
@@ -379,6 +384,8 @@ public slots:
|
||||
void onReplyToMessage();
|
||||
void onReplyForwardPreviewCancel();
|
||||
|
||||
void onStickerPackInfo();
|
||||
|
||||
void onPreviewParse();
|
||||
void onPreviewCheck();
|
||||
void onPreviewTimeout();
|
||||
@@ -391,10 +398,13 @@ public slots:
|
||||
void onPhotoUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
void onDocumentUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
void onThumbDocumentUploaded(MsgId msgId, const MTPInputFile &file, const MTPInputFile &thumb);
|
||||
void onAudioUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
|
||||
void onDocumentProgress(MsgId msgId);
|
||||
void onAudioProgress(MsgId msgId);
|
||||
|
||||
void onDocumentFailed(MsgId msgId);
|
||||
void onAudioFailed(MsgId msgId);
|
||||
|
||||
void onListScroll();
|
||||
void onHistoryToEnd();
|
||||
@@ -440,6 +450,10 @@ public slots:
|
||||
|
||||
void updateStickers();
|
||||
|
||||
void onRecordError();
|
||||
void onRecordDone(QByteArray result, qint32 samples);
|
||||
void onRecordUpdate(qint16 level, qint32 samples);
|
||||
|
||||
private:
|
||||
|
||||
MsgId _replyToId;
|
||||
@@ -473,7 +487,6 @@ private:
|
||||
void stickersGot(const MTPmessages_AllStickers &stickers);
|
||||
bool stickersFailed(const RPCError &error);
|
||||
|
||||
uint64 _lastStickersUpdate;
|
||||
mtpRequestId _stickersUpdateRequest;
|
||||
|
||||
void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0, bool *previewCancelled = 0);
|
||||
@@ -507,10 +520,16 @@ private:
|
||||
FlatButton _send;
|
||||
IconedButton _attachDocument, _attachPhoto, _attachEmoji;
|
||||
MessageField _field;
|
||||
Animation _recordAnim, _recordingAnim;
|
||||
bool _recording, _inRecord, _inField;
|
||||
anim::ivalue a_recordingLevel;
|
||||
int32 _recordingSamples;
|
||||
anim::fvalue a_recordOver, a_recordDown;
|
||||
anim::cvalue a_recordCancel;
|
||||
int32 _recordCancelWidth;
|
||||
|
||||
Dropdown _attachType;
|
||||
EmojiPan _emojiPan;
|
||||
// StickerPan _stickerPan;
|
||||
DragState _attachDrag;
|
||||
DragArea _attachDragDocument, _attachDragPhoto;
|
||||
|
||||
@@ -530,6 +549,7 @@ private:
|
||||
|
||||
bool hiderOffered;
|
||||
|
||||
Animation _showAnim;
|
||||
QPixmap _animCache, _bgAnimCache, _animTopBarCache, _bgAnimTopBarCache;
|
||||
anim::ivalue a_coord, a_bgCoord;
|
||||
anim::fvalue a_alpha, a_bgAlpha;
|
||||
|
||||
@@ -153,6 +153,7 @@ void IntroCode::activate() {
|
||||
callTimer.start(1000);
|
||||
error = "";
|
||||
errorAlpha = anim::fvalue(0);
|
||||
sentCode = QString();
|
||||
show();
|
||||
code.setDisabled(false);
|
||||
code.setFocus();
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} hat {user} entfernt";
|
||||
"lng_action_user_left" = "{from} hat die Gruppe verlassen";
|
||||
"lng_action_user_joined" = "{from} ist der Gruppe beigetreten";
|
||||
"lng_action_user_joined_by_link" = "{from} ist der Gruppe über den Einladungslink beigetreten";
|
||||
"lng_action_user_registered" = "{from} benutzt nun auch Telegram";
|
||||
"lng_action_removed_photo" = "{from} hat das Gruppenbild entfernt";
|
||||
"lng_action_changed_photo" = "{from} hat das Gruppenbild geändert";
|
||||
"lng_action_changed_title" = "{from} hat den Gruppennamen zu «{title}» geändert";
|
||||
"lng_action_created_chat" = "{from} hat die Gruppe «{title}» erstellt";
|
||||
|
||||
"lng_group_invite_bad_link" = "Der Einladungslink ist \nnicht mehr gültig.";
|
||||
"lng_group_invite_want_join" = "Möchtest du der Gruppe «{title}» beitreten?";
|
||||
"lng_group_invite_join" = "Beitreten";
|
||||
|
||||
"lng_group_invite_link" = "Einladungslink";
|
||||
"lng_group_invite_create" = "Neuer Link";
|
||||
"lng_group_invite_about" = "Jeder, der Telegram installiert hat,\nkann anhand dieses Links in deine Gruppe.";
|
||||
"lng_group_invite_create_new" = "Link widerrufen";
|
||||
"lng_group_invite_about_new" = "Dein vorheriger Link wird ungültig,\nwenn du einen neuen erstellst.";
|
||||
"lng_group_invite_copied" = "Der Einladungslink für die Gruppe\nwurde in die Zwischenablage kopiert.";
|
||||
"lng_group_invite_no_room" = "Leider kannst du dieser Gruppe nicht mehr\nbeitreten, da sie bereits voll ist.";
|
||||
|
||||
"lng_forwarded_from" = "Weitergeleitet von";
|
||||
"lng_in_reply_to" = "Antwort auf";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Videodatei";
|
||||
"lng_media_audio" = "Sprachnachricht";
|
||||
|
||||
"lng_emoji_category0" = "Häufig genutzt";
|
||||
"lng_emoji_category1" = "Personen";
|
||||
"lng_emoji_category2" = "Natur";
|
||||
"lng_emoji_category3" = "Essen & Trinken";
|
||||
"lng_emoji_category4" = "Feiern";
|
||||
"lng_emoji_category5" = "Aktivität";
|
||||
"lng_emoji_category6" = "Reisen & Orte";
|
||||
"lng_emoji_category7" = "Objekte & Symbole";
|
||||
|
||||
"lng_switch_stickers" = "Sticker";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Eigene Sticker";
|
||||
"lng_stickers_remove_pack" = "«{sticker_pack}» entfernen?";
|
||||
"lng_stickers_add_pack" = "{count:_not_used_|# Sticker|# Sticker} hinzufügen";
|
||||
"lng_stickers_share_pack" = "Sticker teilen";
|
||||
"lng_stickers_not_found" = "Sticker-Paket nicht gefunden.";
|
||||
"lng_stickers_copied" = "Sticker-Paket Link in die Zwischenablage kopiert.";
|
||||
"lng_stickers_default_set" = "Große Denker";
|
||||
|
||||
"lng_in_dlg_photo" = "Bild";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
"lng_in_dlg_contact" = "Kontakt";
|
||||
@@ -458,6 +491,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Video speichern unter..";
|
||||
"lng_context_open_audio" = "Sprachnachricht öffnen";
|
||||
"lng_context_save_audio" = "Sprachnachricht speichern unter..";
|
||||
"lng_context_pack_info" = "Sticker-Paket";
|
||||
"lng_context_open_file" = "Datei öffnen";
|
||||
"lng_context_save_file" = "Datei speichern als..";
|
||||
"lng_context_forward_file" = "Datei weiterleiten";
|
||||
@@ -527,12 +561,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:Keine Nachrichten|# Nachricht|# Nachrichten}";
|
||||
"lng_search_global_results" = "Globale Suchergebnisse";
|
||||
|
||||
"lng_mediaview_save" = "Download";
|
||||
"lng_media_save_progress" = "{ready} von {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Speichern unter…";
|
||||
"lng_mediaview_copy" = "Kopieren";
|
||||
"lng_mediaview_forward" = "Weiterleiten";
|
||||
"lng_mediaview_delete" = "Löschen";
|
||||
"lng_mediaview_photos_all" = "Alle Fotos anzeigen";
|
||||
"lng_mediaview_files_all" = "Alle Dateien anzeigen";
|
||||
"lng_mediaview_single_photo" = "Bild";
|
||||
"lng_mediaview_group_photo" = "Bild";
|
||||
"lng_mediaview_profile_photo" = "Profilbild";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} von {count}";
|
||||
"lng_mediaview_n_of_count" = "Bild {n} von {count}";
|
||||
"lng_mediaview_doc_image" = "Datei";
|
||||
"lng_mediaview_today" = "heute um {time}";
|
||||
@@ -545,7 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop wurde aktualisiert auf Version {version}\n\n{changes}\n\nGesamter Versionsverlauf:\n{link}";
|
||||
"lng_new_version_minor" = "— Fehlerbehebungen und Softwareoptimierungen";
|
||||
"lng_new_version_text" = "— Linkvorschau: Erhalte eine Zusammenfassung für Tweets, YouTube Videos, Instagram Fotos und sonstigen Inhalten.\n— Zweistufige Bestätigung: Lege ein zusätzliches Kennwort fest, das du für die Anmeldung bei deinem Telegram-Konto benötigst.\n— Sitzungslisten: Betrachte alle deine aktiven Telegram-Sitzungen (auf dem Desktop, Tablet und mobilen Geräten) und beende sie.";
|
||||
"lng_new_version_text" = "— Installiere und teile benutzerdefinierte Sticker-Pakete\n— Neue Emoji und ein Panel für Sticker";
|
||||
|
||||
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";
|
||||
|
||||
|
||||
@@ -161,19 +161,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_crop_profile" = "Elige el cuadrado para tu foto de perfil";
|
||||
"lng_settings_uploading_photo" = "Cargando foto...";
|
||||
|
||||
"lng_username_title" = "Cambiar apodo";
|
||||
"lng_username_about" = "Puedes elegir un apodo en Telegram. \nSi lo haces, otras personas te podrán \nencontrar por ese apodo y contactarte \nsin saber tu número de teléfono.\n\nPuedes usar a-z, 0-9 y guiones bajos.\nLa longitud mínima es de 5 caracteres.";
|
||||
"lng_username_invalid" = "Este apodo es inválido.";
|
||||
"lng_username_occupied" = "Este apodo ya está ocupado.";
|
||||
"lng_username_too_short" = "Este apodo es muy corto.";
|
||||
"lng_username_bad_symbols" = "Este apodo tiene símbolos equivocados.";
|
||||
"lng_username_available" = "Este apodo está disponible.";
|
||||
"lng_username_title" = "Cambiar alias";
|
||||
"lng_username_about" = "Puedes elegir un alias en Telegram. \nSi lo haces, otras personas te podrán \nencontrar por ese alias y contactarte \nsin saber tu número de teléfono.\n\nPuedes usar a-z, 0-9 y guiones bajos.\nLa longitud mínima es de 5 caracteres.";
|
||||
"lng_username_invalid" = "Este alias es inválido.";
|
||||
"lng_username_occupied" = "Este alias ya está ocupado.";
|
||||
"lng_username_too_short" = "Este alias es muy corto.";
|
||||
"lng_username_bad_symbols" = "Este alias tiene símbolos equivocados.";
|
||||
"lng_username_available" = "Este alias está disponible.";
|
||||
"lng_username_not_found" = "No se encontró el usuario @{user}.";
|
||||
|
||||
"lng_settings_section_contact_info" = "Información";
|
||||
"lng_settings_phone_number" = "Número de teléfono:";
|
||||
"lng_settings_username" = "Apodo:";
|
||||
"lng_settings_choose_username" = "Elige un apodo";
|
||||
"lng_settings_username" = "Alias:";
|
||||
"lng_settings_choose_username" = "Elige un alias";
|
||||
|
||||
"lng_settings_section_notify" = "Notificaciones";
|
||||
"lng_settings_desktop_notify" = "Notificaciones de escritorio";
|
||||
@@ -263,7 +263,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_passcode_enter_old" = "Pon tu código actual";
|
||||
"lng_passcode_enter_first" = "Pon un código de acceso";
|
||||
"lng_passcode_enter_new" = "Pon tu nuevo código";
|
||||
"lng_passcode_confirm_new" = "Pon, otra vez, el código";
|
||||
"lng_passcode_confirm_new" = "Repite el nuevo código";
|
||||
"lng_passcode_about" = "Cuando configuras un código, aparece un candado en el menú. Pulsa sobre él para bloquear la aplicación.\n\nNota: si olvidas el código, tendrás que reiniciar la sesión en Telegram Desktop.";
|
||||
"lng_passcode_differ" = "Los códigos de acceso son diferentes";
|
||||
"lng_passcode_wrong" = "Código de acceso equivocado";
|
||||
@@ -281,7 +281,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_cloud_password_enter_old" = "Pon la vieja contraseña";
|
||||
"lng_cloud_password_enter_first" = "Pon una contraseña";
|
||||
"lng_cloud_password_enter_new" = "Pon la nueva contraseña";
|
||||
"lng_cloud_password_confirm_new" = "Pon, otra vez, la contraseña";
|
||||
"lng_cloud_password_confirm_new" = "Repite la nueva contraseña";
|
||||
"lng_cloud_password_hint" = "Pon una pista para la contraseña";
|
||||
"lng_cloud_password_bad" = "La contraseña y la pista no pueden ser iguales.";
|
||||
"lng_cloud_password_email" = "Pon un e-mail de recuperación";
|
||||
@@ -307,7 +307,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_connection_tcp_proxy_rb" = "TCP con socks5-proxy personalizado";
|
||||
"lng_connection_host_ph" = "Nombre del host";
|
||||
"lng_connection_port_ph" = "Puerto";
|
||||
"lng_connection_user_ph" = "Apodo";
|
||||
"lng_connection_user_ph" = "Alias";
|
||||
"lng_connection_password_ph" = "Contraseña";
|
||||
"lng_connection_save" = "Guardar";
|
||||
"lng_settings_show_sessions" = "Mostrar todas las sesiones";
|
||||
@@ -350,7 +350,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_kick" = "Expulsar";
|
||||
"lng_profile_sure_kick" = "¿Quieres expulsar a {user} del grupo?";
|
||||
"lng_profile_loading" = "Cargando...";
|
||||
"lng_profile_shared_media" = "Multimedia compartida";
|
||||
"lng_profile_shared_media" = "Todos los archivos";
|
||||
"lng_profile_no_media" = "No hay multimedia en esta conversación.";
|
||||
"lng_profile_photos" = "{count:_not_used_|# foto|# fotos} »";
|
||||
"lng_profile_photos_header" = "Todas las fotos";
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} expulsó a {user}";
|
||||
"lng_action_user_left" = "{from} dejó el grupo";
|
||||
"lng_action_user_joined" = "{from} se unió al grupo";
|
||||
"lng_action_user_joined_by_link" = "{from} se unió al grupo con un enlace de invitación";
|
||||
"lng_action_user_registered" = "{from} se acaba de unir a Telegram";
|
||||
"lng_action_removed_photo" = "{from} quitó la foto del grupo";
|
||||
"lng_action_changed_photo" = "{from} cambió la foto del grupo";
|
||||
"lng_action_changed_title" = "{from} cambió el nombre del grupo a «{title}»";
|
||||
"lng_action_created_chat" = "{from} creó el grupo «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "El enlace de invitación está \nroto o ha expirado.";
|
||||
"lng_group_invite_want_join" = "¿Quieres unirte al grupo «{title}»?";
|
||||
"lng_group_invite_join" = "Unirme";
|
||||
|
||||
"lng_group_invite_link" = "Invitación";
|
||||
"lng_group_invite_create" = "Crear un enlace de invitación";
|
||||
"lng_group_invite_about" = "Los usuarios de Telegram podrán unirse\na tu grupo a través de este enlace.";
|
||||
"lng_group_invite_create_new" = "Anular enlace de invitación";
|
||||
"lng_group_invite_about_new" = "El enlace previo será desactivado\ny generaremos uno nuevo para ti.";
|
||||
"lng_group_invite_copied" = "Enlace de invitación copiado al portapapeles.";
|
||||
"lng_group_invite_no_room" = "No puedes unirte a este grupo porque\nhay muchos miembros en él.";
|
||||
|
||||
"lng_forwarded_from" = "Reenviado desde";
|
||||
"lng_in_reply_to" = "Respondiendo a";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Vídeo";
|
||||
"lng_media_audio" = "Mensaje de voz";
|
||||
|
||||
"lng_emoji_category0" = "Usados frecuentemente";
|
||||
"lng_emoji_category1" = "Gente";
|
||||
"lng_emoji_category2" = "Naturaleza";
|
||||
"lng_emoji_category3" = "Comida y bebidas";
|
||||
"lng_emoji_category4" = "Celebración";
|
||||
"lng_emoji_category5" = "Actividad";
|
||||
"lng_emoji_category6" = "Viajes y lugares";
|
||||
"lng_emoji_category7" = "Objetos y símbolos";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Stickers personalizados";
|
||||
"lng_stickers_remove_pack" = "¿Quitar «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Añadir {count:_not_used_|# sticker|# stickers}";
|
||||
"lng_stickers_share_pack" = "Compartir stickers";
|
||||
"lng_stickers_not_found" = "Pack de stickers no encontrado.";
|
||||
"lng_stickers_copied" = "Enlace del pack de stickers copiado al portapapeles.";
|
||||
"lng_stickers_default_set" = "Grandes personajes";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Vídeo";
|
||||
"lng_in_dlg_contact" = "Contacto";
|
||||
@@ -444,7 +477,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_open_hashtag" = "Buscar por hashtag";
|
||||
"lng_context_copy_hashtag" = "Copiar hashtag";
|
||||
"lng_context_open_mention" = "Abrir perfil";
|
||||
"lng_context_copy_mention" = "Copiar apodo";
|
||||
"lng_context_copy_mention" = "Copiar alias";
|
||||
"lng_context_open_image" = "Abrir imagen";
|
||||
"lng_context_save_image" = "Guardar como...";
|
||||
"lng_context_forward_image" = "Reenviar imagen";
|
||||
@@ -458,6 +491,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Guardar como...";
|
||||
"lng_context_open_audio" = "Abrir audio";
|
||||
"lng_context_save_audio" = "Guardar como...";
|
||||
"lng_context_pack_info" = "Información del pack";
|
||||
"lng_context_open_file" = "Abrir archivo";
|
||||
"lng_context_save_file" = "Guardar como...";
|
||||
"lng_context_forward_file" = "Reenviar archivo";
|
||||
@@ -527,12 +561,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:No se encontraron mensajes|Found # mensaje|Found # mensajes}";
|
||||
"lng_search_global_results" = "Resultados de la búsqueda global";
|
||||
|
||||
"lng_mediaview_save" = "Descargar";
|
||||
"lng_media_save_progress" = "{ready} de {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Guardar como...";
|
||||
"lng_mediaview_copy" = "Copiar";
|
||||
"lng_mediaview_forward" = "Reenviar";
|
||||
"lng_mediaview_delete" = "Eliminar";
|
||||
"lng_mediaview_photos_all" = "Ver todas las fotos";
|
||||
"lng_mediaview_files_all" = "Ver todos los archivos";
|
||||
"lng_mediaview_single_photo" = "Foto";
|
||||
"lng_mediaview_group_photo" = "Foto del grupo";
|
||||
"lng_mediaview_profile_photo" = "Foto de perfil";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} de {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} de {count}";
|
||||
"lng_mediaview_doc_image" = "Archivo";
|
||||
"lng_mediaview_today" = "hoy a las {time}";
|
||||
@@ -545,7 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop fue actualizada a la versión {version}\n\n{changes}\n\nEl historial completo está disponible aquí:\n{link}";
|
||||
"lng_new_version_minor" = "— Corrección de errores y otras mejoras menores";
|
||||
"lng_new_version_text" = "— Vista previa de enlaces de Twitter, YouTube, Instagram y otros\n— Verificación en dos pasos\n— Ve todas tus sesiones de Telegram y cierra sesiones específicas";
|
||||
"lng_new_version_text" = "— Añadido el soporte para packs de stickers\n— Nuevas pestañas de emojis y stickers";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_section_cache" = "Archivio locale";
|
||||
"lng_settings_no_data_cached" = "Non ci sono dati nella cache!";
|
||||
"lng_settings_images_cached" = "{count:_not_used_|# immagine|# immagini}, {size}";
|
||||
"lng_settings_audios_cached" = "{count:_not_used_|# messaggio vocale|# messaggi vocali}, {size}";
|
||||
"lng_settings_audios_cached" = "{count:_not_used_|# nota vocale|# note vocali}, {size}";
|
||||
"lng_local_storage_clear" = "Elimina tutto";
|
||||
"lng_local_storage_clearing" = "Eliminando..";
|
||||
"lng_local_storage_cleared" = "Eliminato!";
|
||||
@@ -287,7 +287,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_cloud_password_email" = "Inserisci email di recupero";
|
||||
"lng_cloud_password_bad_email" = "E-mail non valida, riprova con un'altra.";
|
||||
"lng_cloud_password_about" = "La password verrà richiesta quando ti connetti da un nuovo dispositivo in aggiunta al codice.";
|
||||
"lng_cloud_password_about_recover" = "Attenzione! Sei sicuro di non voler \naggiungere un'e-mail di recupero?\n\nSe dimentichi la tua password, perderai\nl'accesso al tuo account Telegram.";
|
||||
"lng_cloud_password_about_recover" = "Attenzione! Sei sicuro di non voler\naggiungere un'e-mail di recupero?\n\nSe dimentichi la tua password, perderai\nl'accesso al tuo account Telegram.";
|
||||
"lng_cloud_password_almost" = "Abbiamo inviato un link di conferma\nall'e-mail che ci hai fornito.\n\nLa verifica in due passaggi sarà attivata\nnon appena aprirai quel link.";
|
||||
"lng_cloud_password_was_set" = "Verifica in due passaggi abilitata.";
|
||||
"lng_cloud_password_updated" = "La tua password è stata aggiornata.";
|
||||
@@ -347,7 +347,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_set_group_photo" = "Imposta foto";
|
||||
"lng_profile_add_participant" = "Aggiungi membro";
|
||||
"lng_profile_delete_and_exit" = "Esci";
|
||||
"lng_profile_kick" = "Espelli";
|
||||
"lng_profile_kick" = "Rimuovi";
|
||||
"lng_profile_sure_kick" = "Espellere {user} dal gruppo?";
|
||||
"lng_profile_loading" = "Caricamento..";
|
||||
"lng_profile_shared_media" = "Media condivisi";
|
||||
@@ -358,8 +358,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_videos_header" = "Panoramica video";
|
||||
"lng_profile_files" = "{count:_not_used_|# file|# file} »";
|
||||
"lng_profile_files_header" = "Panoramica file";
|
||||
"lng_profile_audios" = "{count:_not_used_|# messaggio vocale|# messaggi vocali} »";
|
||||
"lng_profile_audios_header" = "Panoramica messaggi vocali";
|
||||
"lng_profile_audios" = "{count:_not_used_|# nota vocale|# note vocali} »";
|
||||
"lng_profile_audios_header" = "Panoramica note vocali";
|
||||
"lng_profile_show_all_types" = "Mostra tutti i tipi";
|
||||
"lng_profile_copy_phone" = "Copia numero di telefono";
|
||||
|
||||
@@ -379,15 +379,28 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_message_empty" = "(vuoto)";
|
||||
|
||||
"lng_action_add_user" = "{from} ha aggiunto {user}";
|
||||
"lng_action_kick_user" = "{from} ha espulso {user}";
|
||||
"lng_action_kick_user" = "{from} ha rimosso {user}";
|
||||
"lng_action_user_left" = "{from} ha lasciato il gruppo";
|
||||
"lng_action_user_joined" = "{from} si è unito al gruppo";
|
||||
"lng_action_user_joined_by_link" = "{from} si è unito al gruppo tramite link di invito";
|
||||
"lng_action_user_registered" = "{from} ha iniziato a usare Telegram";
|
||||
"lng_action_removed_photo" = "{from} ha rimosso la foto del gruppo";
|
||||
"lng_action_changed_photo" = "{from} ha cambiato la foto del gruppo";
|
||||
"lng_action_changed_title" = "{from} ha cambiato il nome del gruppo in «{title}»";
|
||||
"lng_action_created_chat" = "{from} ha creato il gruppo «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "Questo link di invito non funziona\no è scaduto.";
|
||||
"lng_group_invite_want_join" = "Vuoi unirti al gruppo «{title}»?";
|
||||
"lng_group_invite_join" = "Unisciti";
|
||||
|
||||
"lng_group_invite_link" = "Link di invito";
|
||||
"lng_group_invite_create" = "Crea un link di invito";
|
||||
"lng_group_invite_about" = "Gli utenti di Telegram saranno in grado di \naggiungersi al tuo gruppo aprendo il link.";
|
||||
"lng_group_invite_create_new" = "Revoca link";
|
||||
"lng_group_invite_about_new" = "Il link precedente smetterà di funzionare\ne ne sarà creato uno nuovo per te.";
|
||||
"lng_group_invite_copied" = "Link di invito copiato negli appunti.";
|
||||
"lng_group_invite_no_room" = "Impossibile unirsi a questo gruppo\nperché ci sono già troppi membri.";
|
||||
|
||||
"lng_forwarded_from" = "Inoltrato da";
|
||||
"lng_in_reply_to" = "In risposta a";
|
||||
|
||||
@@ -399,13 +412,33 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_type_photos" = "Foto";
|
||||
"lng_media_type_videos" = "Video";
|
||||
"lng_media_type_files" = "File";
|
||||
"lng_media_type_audios" = "Messaggi vocali";
|
||||
"lng_media_type_audios" = "Note vocali";
|
||||
|
||||
"lng_media_open_with" = "Apri con";
|
||||
"lng_media_download" = "Download";
|
||||
"lng_media_cancel" = "Annulla";
|
||||
"lng_media_video" = "Video";
|
||||
"lng_media_audio" = "Messaggio vocale";
|
||||
"lng_media_audio" = "Nota vocale";
|
||||
|
||||
"lng_emoji_category0" = "Utilizzate di frequente";
|
||||
"lng_emoji_category1" = "Persone";
|
||||
"lng_emoji_category2" = "Natura";
|
||||
"lng_emoji_category3" = "Cibo e bevande";
|
||||
"lng_emoji_category4" = "Festeggiamenti";
|
||||
"lng_emoji_category5" = "Attività";
|
||||
"lng_emoji_category6" = "Viaggi e luoghi";
|
||||
"lng_emoji_category7" = "Oggetti e simboli";
|
||||
|
||||
"lng_switch_stickers" = "Sticker";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Sticker personalizzati";
|
||||
"lng_stickers_remove_pack" = "Rimuovere «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Aggiungi {count:_not_used_|# sticker|# stickers}";
|
||||
"lng_stickers_share_pack" = "Condividi sticker";
|
||||
"lng_stickers_not_found" = "Pacchetto di sticker non trovato.";
|
||||
"lng_stickers_copied" = "Link degli sticker copiato negli appunti.";
|
||||
"lng_stickers_default_set" = "Grandi menti";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
@@ -458,6 +491,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Salva video come..";
|
||||
"lng_context_open_audio" = "Apri audio";
|
||||
"lng_context_save_audio" = "Salva audio come..";
|
||||
"lng_context_pack_info" = "Info pacchetto";
|
||||
"lng_context_open_file" = "Apri file";
|
||||
"lng_context_save_file" = "Salva file come..";
|
||||
"lng_context_forward_file" = "Inoltra file";
|
||||
@@ -527,12 +561,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:Nessun messaggio trovato|# messaggio trovato|# messaggi trovati}";
|
||||
"lng_search_global_results" = "Risultati ricerca globale";
|
||||
|
||||
"lng_mediaview_save" = "Download";
|
||||
"lng_media_save_progress" = "{ready} di {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Salva come..";
|
||||
"lng_mediaview_copy" = "Copia";
|
||||
"lng_mediaview_forward" = "Inoltra";
|
||||
"lng_mediaview_delete" = "Elimina";
|
||||
"lng_mediaview_photos_all" = "Visualizza tutte le foto";
|
||||
"lng_mediaview_files_all" = "Visualizza tutti i file";
|
||||
"lng_mediaview_single_photo" = "Foto singola";
|
||||
"lng_mediaview_group_photo" = "Foto gruppo";
|
||||
"lng_mediaview_profile_photo" = "Foto profilo";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} di {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} di {count}";
|
||||
"lng_mediaview_doc_image" = "File";
|
||||
"lng_mediaview_today" = "oggi alle {time}";
|
||||
@@ -545,7 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli update è disponibile qui:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fix e altri miglioramenti minori";
|
||||
"lng_new_version_text" = "— Anteprima dei link per Twitter, Youtube, Instagram e altri contenuti.\n— Verifica in due passaggi.\n— Visualizza le sessioni attive di Telegram e termina sessioni specifiche.";
|
||||
"lng_new_version_text" = "— Aggiunto supporto ai pacchetti di sticker\n— Nuove emoji e pannello sticker";
|
||||
|
||||
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";
|
||||
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} 님께서 {user} 님을 추방하셨습니다.";
|
||||
"lng_action_user_left" = "{from} 님이 그룹을 나가셨습니다.";
|
||||
"lng_action_user_joined" = "{from} 님이 그룹에 들어오셨습니다.";
|
||||
"lng_action_user_joined_by_link" = "초대링크를 타고 {from} 님이 그룹에 참여하였습니다.";
|
||||
"lng_action_user_registered" = "{from} 님이 텔레그램에 가입하셨습니다.";
|
||||
"lng_action_removed_photo" = "{from} 님이 그룹사진을 삭제하셨습니다.";
|
||||
"lng_action_changed_photo" = "{from} 님이 그룹사진을 변경하셨습니다.";
|
||||
"lng_action_changed_title" = "{from} 님이 그룹이름을 «{title}» 로 바꾸셨습니다.";
|
||||
"lng_action_created_chat" = "{from} 님이 그룹 «{title}» 을 생성하셨습니다.";
|
||||
|
||||
"lng_group_invite_bad_link" = "초대링크가 깨졌거나,\n폐기되었습니다.";
|
||||
"lng_group_invite_want_join" = "«{title}» 그룹방에 참여하시겠습니까?";
|
||||
"lng_group_invite_join" = "참여";
|
||||
|
||||
"lng_group_invite_link" = "초대링크";
|
||||
"lng_group_invite_create" = "초대링크 생성";
|
||||
"lng_group_invite_about" = "이 링크를 통하여,\n그룹방에 초대가 가능합니다.";
|
||||
"lng_group_invite_create_new" = "초대링크 폐기";
|
||||
"lng_group_invite_about_new" = "기존 링크는 비활성화 될 예정이며,\n새로운 링크가 재생성이 될 예정입니다.";
|
||||
"lng_group_invite_copied" = "클립보드에 초대링크가 복사 되었습니다.";
|
||||
"lng_group_invite_no_room" = "그룹방 인원 최대치가 도달하여\n더이상 참여가 안됩니다.";
|
||||
|
||||
"lng_forwarded_from" = "전달받음";
|
||||
"lng_in_reply_to" = "다음 유저에게 답장 :";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "비디오 파일";
|
||||
"lng_media_audio" = "음성 메시지";
|
||||
|
||||
"lng_emoji_category0" = "자주 사용";
|
||||
"lng_emoji_category1" = "사람";
|
||||
"lng_emoji_category2" = "자연";
|
||||
"lng_emoji_category3" = "음식점";
|
||||
"lng_emoji_category4" = "기념일";
|
||||
"lng_emoji_category5" = "활동";
|
||||
"lng_emoji_category6" = "여행 및 장소";
|
||||
"lng_emoji_category7" = "오브제 & 상징";
|
||||
|
||||
"lng_switch_stickers" = "스티커";
|
||||
"lng_switch_emoji" = "이모티콘";
|
||||
|
||||
"lng_custom_stickers" = "커스텀 스티커";
|
||||
"lng_stickers_remove_pack" = "«{sticker_pack}»을 제거하시겠습니까?";
|
||||
"lng_stickers_add_pack" = "{count:_not_used_|# 스티커|# 스티커} 추가";
|
||||
"lng_stickers_share_pack" = "스티커 공유";
|
||||
"lng_stickers_not_found" = "스티커 팩을 찾을 수 없습니다.";
|
||||
"lng_stickers_copied" = "클립보드에 스티커 팩 링크가 복사 되었습니다.";
|
||||
"lng_stickers_default_set" = "Great Minds";
|
||||
|
||||
"lng_in_dlg_photo" = "사진";
|
||||
"lng_in_dlg_video" = "비디오";
|
||||
"lng_in_dlg_contact" = "연락처";
|
||||
@@ -458,6 +491,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "비디오 이름을 다른이름으로 저장.";
|
||||
"lng_context_open_audio" = "음성메시지 열기";
|
||||
"lng_context_save_audio" = "음성메시지를 다른 이름으로 저장..";
|
||||
"lng_context_pack_info" = "팩 정보";
|
||||
"lng_context_open_file" = "파일 열기";
|
||||
"lng_context_save_file" = "파일을 다른 이름으로 저장..";
|
||||
"lng_context_forward_file" = "파일 전달";
|
||||
@@ -527,12 +561,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:메시지를 찾을 수 없습니다.|# 개의 메시지를 찾았습니다.|# 개의 메시지를 찾았습니다.}";
|
||||
"lng_search_global_results" = "아이디 검색 결과";
|
||||
|
||||
"lng_mediaview_save" = "다운로드";
|
||||
"lng_media_save_progress" = "{ready} / {total} {mb}";
|
||||
"lng_mediaview_save_as" = "다른 이름으로 저장하기";
|
||||
"lng_mediaview_copy" = "복사하기";
|
||||
"lng_mediaview_forward" = "전달";
|
||||
"lng_mediaview_delete" = "삭제";
|
||||
"lng_mediaview_photos_all" = "모든 사진 보기";
|
||||
"lng_mediaview_files_all" = "모든 파일 보기";
|
||||
"lng_mediaview_single_photo" = "단일 사진";
|
||||
"lng_mediaview_group_photo" = "그룹 사진";
|
||||
"lng_mediaview_profile_photo" = "프로필 사진";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} 중 {count}";
|
||||
"lng_mediaview_n_of_count" = "사진 {n} 중 {count}";
|
||||
"lng_mediaview_doc_image" = "파일";
|
||||
"lng_mediaview_today" = "오늘 {time}";
|
||||
@@ -545,7 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}";
|
||||
"lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상";
|
||||
"lng_new_version_text" = "— 트윗, 유투브 비디오, 인스터그램 사진등의 프리뷰 기능\n— 2단계 인증\n— 활성화된 모든 텔레그램 세션 확인 및 특정 세션 종료 기능";
|
||||
"lng_new_version_text" = "— 스티커 팩 기능\n— 신규 이모티콘 및 스티커 패널";
|
||||
|
||||
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_passcode_enter_first" = "Toegangscode invoeren";
|
||||
"lng_passcode_enter_new" = "Nieuwe toegangscode invoeren";
|
||||
"lng_passcode_confirm_new" = "Toegangscode opnieuw invoeren";
|
||||
"lng_passcode_about" = "Als je een toegangscode instelt verschijnt er een slotje op de chatspagina, klikerop om de app te vergrendelen\n\nLet op: als je de toegangscode vergeet, zul je opnieuw in moeten loggen.";
|
||||
"lng_passcode_about" = "Als je een toegangscode instelt verschijnt er een slotje op de chatspagina, klik erop om de app te vergrendelen\n\nLet op: als je de toegangscode vergeet, zul je opnieuw in moeten loggen.";
|
||||
"lng_passcode_differ" = "Toegangscodes zijn verschillend";
|
||||
"lng_passcode_wrong" = "Onjuiste toegangscode";
|
||||
"lng_passcode_is_same" = "Toegangscode is niet gewijzigd";
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} heeft {user} verwijderd";
|
||||
"lng_action_user_left" = "{from} heeft de groep verlaten";
|
||||
"lng_action_user_joined" = "{from} neemt deel aan de groep";
|
||||
"lng_action_user_joined_by_link" = "{from} neemt deel aan de groep via uitnodigingslink";
|
||||
"lng_action_user_registered" = "{from} heeft nu Telegram";
|
||||
"lng_action_removed_photo" = "{from} heeft de groepsafbeelding verwijderd";
|
||||
"lng_action_changed_photo" = "{from} heeft de groepsafbeelding gewijzigd";
|
||||
"lng_action_changed_title" = "{from} heeft de groepsnaam gewijzigd naar «{title}»";
|
||||
"lng_action_created_chat" = "{from} heeft de groep «{title}» gemaakt";
|
||||
|
||||
"lng_group_invite_bad_link" = "Deze uitnodigingslink is \ndefect of verlopen.";
|
||||
"lng_group_invite_want_join" = "Wil je deelnemen aan de groep «{title}»?";
|
||||
"lng_group_invite_join" = "Deelnemen";
|
||||
|
||||
"lng_group_invite_link" = "Uitnodigingslink";
|
||||
"lng_group_invite_create" = "Uitnodigingslink maken";
|
||||
"lng_group_invite_about" = "Gebruikers kunnen aan je groep \ndeelnemen met deze link.";
|
||||
"lng_group_invite_create_new" = "Uitnodigingslink intrekken";
|
||||
"lng_group_invite_about_new" = "Je uitnodigingslink zal inactief worden\neen nieuwe link zal worden gegenereerd.";
|
||||
"lng_group_invite_copied" = "Link gekopieerd naar klembord.";
|
||||
"lng_group_invite_no_room" = "Sorry, deze groep is al vol.";
|
||||
|
||||
"lng_forwarded_from" = "Doorgestuurd van";
|
||||
"lng_in_reply_to" = "Antwoord op";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Video";
|
||||
"lng_media_audio" = "Spraakbericht";
|
||||
|
||||
"lng_emoji_category0" = "Veelgebruikt";
|
||||
"lng_emoji_category1" = "Mensen";
|
||||
"lng_emoji_category2" = "Natuur";
|
||||
"lng_emoji_category3" = "Eten & drinken";
|
||||
"lng_emoji_category4" = "Feestelijkheid";
|
||||
"lng_emoji_category5" = "Actviteit";
|
||||
"lng_emoji_category6" = "Reizen & plekken";
|
||||
"lng_emoji_category7" = "Objecten & symbolen";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Aangepaste stickers";
|
||||
"lng_stickers_remove_pack" = " «{sticker_pack}» verwijderen?";
|
||||
"lng_stickers_add_pack" = "{count:_not_used_|# Sticker|# Stickers} toevoegen";
|
||||
"lng_stickers_share_pack" = "Stickers delen";
|
||||
"lng_stickers_not_found" = "Stickerbundel niet gevonden.";
|
||||
"lng_stickers_copied" = "Stickerbundel-link gekopieerd naar klembord";
|
||||
"lng_stickers_default_set" = "Grote geesten";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
"lng_in_dlg_contact" = "Contact";
|
||||
@@ -458,6 +491,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Video opslaan als";
|
||||
"lng_context_open_audio" = "Geluidsbestand openen";
|
||||
"lng_context_save_audio" = "Geluidsbestand opslaan als";
|
||||
"lng_context_pack_info" = "Bundelinformatie";
|
||||
"lng_context_open_file" = "Bestand openen";
|
||||
"lng_context_save_file" = "Bestand opslaan als";
|
||||
"lng_context_forward_file" = "Bestand doorsturen";
|
||||
@@ -527,12 +561,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:geen berichten gevonden|# berichten gevonden|# berichten gevonden}";
|
||||
"lng_search_global_results" = "Wereldwijde zoekresultaten";
|
||||
|
||||
"lng_mediaview_save" = "Downloaden";
|
||||
"lng_media_save_progress" = "{ready} van {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Opslaan als";
|
||||
"lng_mediaview_copy" = "Kopiëren";
|
||||
"lng_mediaview_forward" = "Doorsturen";
|
||||
"lng_mediaview_delete" = "Verwijder";
|
||||
"lng_mediaview_photos_all" = "Alle foto's weergeven";
|
||||
"lng_mediaview_files_all" = "Alle bestanden weergeven";
|
||||
"lng_mediaview_single_photo" = "Foto";
|
||||
"lng_mediaview_group_photo" = "Groepsafbeelding";
|
||||
"lng_mediaview_profile_photo" = "Profielfoto";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} van {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} van {count}";
|
||||
"lng_mediaview_doc_image" = "Bestand";
|
||||
"lng_mediaview_today" = "vandaag om {time}";
|
||||
@@ -545,7 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram is bijgewerkt naar versie {version}\n\n{changes} \n\nVolledige versiegeschiedenis is hier te vinden:\n{link}";
|
||||
"lng_new_version_minor" = "— Probleemoplossing en andere kleine verbeteringen";
|
||||
"lng_new_version_text" = "— Link-preview voor Twitter, YouTube, Instagram en diverse andere links\n— Twee-staps-verificatie\n— Bekijk al je Telegram-sessies, beëindig specifieke sessies.";
|
||||
"lng_new_version_text" = "— Ondersteuning voor stickerbundels toegevoegd\n— Nieuw emoji- en stickerspaneel";
|
||||
|
||||
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";
|
||||
|
||||
|
||||
@@ -371,10 +371,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_failed_add_participant" = "Não foi possível adicionar o usuário. Tente novamente mais tarde.";
|
||||
|
||||
"lng_sure_delete_contact" = "Você tem certeza que deseja deletar {contact} da sua lista de contatos?";
|
||||
"lng_sure_delete_history" = "Você tem certeza que deseja deletar todo o seu histórico de mensagens com {contact}?\n\nEssa ação não pode ser desfeita.";
|
||||
"lng_sure_delete_contact" = "Você tem certeza que deseja remover {contact} da sua lista de contatos?";
|
||||
"lng_sure_delete_history" = "Você tem certeza que deseja apagar todo o seu histórico de mensagens com {contact}?\n\nEssa ação não pode ser desfeita.";
|
||||
|
||||
"lng_sure_delete_and_exit" = "Você tem certeza que deseja deletar todo o seu histórico de mensagens e deixar «{group}»?\n\nEssa ação não pode ser desfeita.";
|
||||
"lng_sure_delete_and_exit" = "Você tem certeza que deseja apagar todo o seu histórico de mensagens e deixar «{group}»?\n\nEssa ação não pode ser desfeita.";
|
||||
|
||||
"lng_message_empty" = "(vazio)";
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} removeu {user}";
|
||||
"lng_action_user_left" = "{from} deixou o grupo";
|
||||
"lng_action_user_joined" = "{from} entrou no grupo";
|
||||
"lng_action_user_joined_by_link" = "{from} entrou no grupo via link de convite";
|
||||
"lng_action_user_registered" = "{from} entrou para o Telegram";
|
||||
"lng_action_removed_photo" = "{from} removeu a foto do grupo";
|
||||
"lng_action_changed_photo" = "{from} alterou a foto do grupo";
|
||||
"lng_action_changed_title" = "{from} alterou o título do grupo para «{title}»";
|
||||
"lng_action_created_chat" = "{from} criou o grupo «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "Link de convite quebrado\nou expirado.";
|
||||
"lng_group_invite_want_join" = "Você deseja entrar no grupo «{title}»?";
|
||||
"lng_group_invite_join" = "Entrar";
|
||||
|
||||
"lng_group_invite_link" = "Link de convite";
|
||||
"lng_group_invite_create" = "Criar um link de convite";
|
||||
"lng_group_invite_about" = "Usuários do Telegram poderão entrar\nem seu grupo clicando no link.";
|
||||
"lng_group_invite_create_new" = "Desativar link";
|
||||
"lng_group_invite_about_new" = "Seu link de convite será desativado\ne iremos gerar um novo link para você.";
|
||||
"lng_group_invite_copied" = "Link copiado para área de transferência.";
|
||||
"lng_group_invite_no_room" = "Não foi possível entrar no grupo porque\nele já possui muitos membros.";
|
||||
|
||||
"lng_forwarded_from" = "Encaminhado de";
|
||||
"lng_in_reply_to" = "Em resposta a";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Arquivo de vídeo";
|
||||
"lng_media_audio" = "Mensagem de voz";
|
||||
|
||||
"lng_emoji_category0" = "Frequentemente usado";
|
||||
"lng_emoji_category1" = "Pessoas";
|
||||
"lng_emoji_category2" = "Natureza";
|
||||
"lng_emoji_category3" = "Comidas e Bebidas";
|
||||
"lng_emoji_category4" = "Celebração";
|
||||
"lng_emoji_category5" = "Atividade";
|
||||
"lng_emoji_category6" = "Viagens e Lugares";
|
||||
"lng_emoji_category7" = "Objetos e Símbolos";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Stickers customizados";
|
||||
"lng_stickers_remove_pack" = "Remover «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Adicionar {count:_not_used_|# Sticker|# Stickers}";
|
||||
"lng_stickers_share_pack" = "Compartilhar Stickers";
|
||||
"lng_stickers_not_found" = "Pacote de sticker não encontrado.";
|
||||
"lng_stickers_copied" = "Link copiado para a àrea de transferência.";
|
||||
"lng_stickers_default_set" = "Grandes Mentes";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Vídeo";
|
||||
"lng_in_dlg_contact" = "Contato";
|
||||
@@ -458,6 +491,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Salvar Vídeo Como..";
|
||||
"lng_context_open_audio" = "Abrir Áudio";
|
||||
"lng_context_save_audio" = "Salvar Áudio Como..";
|
||||
"lng_context_pack_info" = "Informação do pacote";
|
||||
"lng_context_open_file" = "Abrir Arquivo";
|
||||
"lng_context_save_file" = "Salvar Arquivo Como..";
|
||||
"lng_context_forward_file" = "Encaminhar Arquivo";
|
||||
@@ -527,12 +561,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:Nenhuma mensagem encontrada|Encontrada # mensagem|Encontradas # mensagens}";
|
||||
"lng_search_global_results" = "Resultados da busca global";
|
||||
|
||||
"lng_mediaview_save" = "Baixar";
|
||||
"lng_media_save_progress" = "{ready} de {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Salvar Como..";
|
||||
"lng_mediaview_copy" = "Copiar";
|
||||
"lng_mediaview_forward" = "Encaminhar";
|
||||
"lng_mediaview_delete" = "Apagar";
|
||||
"lng_mediaview_photos_all" = "Visualizar Fotos";
|
||||
"lng_mediaview_files_all" = "Visualizar Arquivos";
|
||||
"lng_mediaview_single_photo" = "Foto Única";
|
||||
"lng_mediaview_group_photo" = "Grupo de Fotos";
|
||||
"lng_mediaview_profile_photo" = "Foto de Perfil";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} de {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} de {count}";
|
||||
"lng_mediaview_doc_image" = "Arquivo";
|
||||
"lng_mediaview_today" = "hoje às {time}";
|
||||
@@ -545,7 +584,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop foi atualizado para a versão {version}\n\n{changes}\n\nHistórico completo de mudanças disponível aqui:\n{link}";
|
||||
"lng_new_version_minor" = "— Resolução de bugs e outras menores melhorias";
|
||||
"lng_new_version_text" = "— Pré-visualização de links para o Twitter, YouTube, Instagram e alguns outros links\n— Verificação em duas etapas\n— Visualizar todas as suas sessões do Telegram, encerrar sessões específicas.";
|
||||
"lng_new_version_text" = "— Suporte para os pacotes de sticker adicionado\n— Novo painel de sticker e emoji";
|
||||
|
||||
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "gui/filedialog.h"
|
||||
|
||||
BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : QWidget(parent), w(w), _hidden(0),
|
||||
aBackground(0), aBackgroundFunc(anim::easeOutCirc), hiding(false), shadow(st::boxShadow) {
|
||||
aBackground(0), aBackgroundFunc(anim::easeOutCirc), hiding(false), shadow(st::boxShadow) {
|
||||
w->setParent(this);
|
||||
setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
|
||||
aBackground.start(1);
|
||||
@@ -54,7 +54,7 @@ void BackgroundWidget::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(rect(), st::layerBG->b);
|
||||
|
||||
p.setOpacity(aBackground.current());
|
||||
shadow.paint(p, w->boxRect());
|
||||
shadow.paint(p, w->boxRect(), st::boxShadowShift);
|
||||
}
|
||||
|
||||
void BackgroundWidget::keyPressEvent(QKeyEvent *e) {
|
||||
|
||||
@@ -37,6 +37,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
QByteArray data;
|
||||
PeerId peer;
|
||||
uint64 id, thumbId = 0;
|
||||
int32 duration = 0;
|
||||
QString thumbExt = "jpg";
|
||||
ToPrepareMediaType type;
|
||||
bool animated = false;
|
||||
@@ -53,6 +54,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
peer = list.front().peer;
|
||||
id = list.front().id;
|
||||
type = list.front().type;
|
||||
duration = list.front().duration;
|
||||
ctrlShiftEnter = list.front().ctrlShiftEnter;
|
||||
replyTo = list.front().replyTo;
|
||||
}
|
||||
@@ -85,29 +87,34 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
filename = info.fileName();
|
||||
filesize = info.size();
|
||||
} else if (!data.isEmpty()) {
|
||||
img = App::readImage(data, 0, true, &animated);
|
||||
if (type == ToPrepareAuto) {
|
||||
if (!img.isNull() && data.size() < MaxUploadPhotoSize) {
|
||||
type = ToPreparePhoto;
|
||||
} else if (data.size() < MaxUploadDocumentSize) {
|
||||
type = ToPrepareDocument;
|
||||
} else {
|
||||
img = QImage();
|
||||
if (type != ToPrepareAudio) {
|
||||
img = App::readImage(data, 0, true, &animated);
|
||||
if (type == ToPrepareAuto) {
|
||||
if (!img.isNull() && data.size() < MaxUploadPhotoSize) {
|
||||
type = ToPreparePhoto;
|
||||
} else if (data.size() < MaxUploadDocumentSize) {
|
||||
type = ToPrepareDocument;
|
||||
} else {
|
||||
img = QImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
MimeType mimeType = mimeTypeForData(data);
|
||||
if (type == ToPrepareDocument) {
|
||||
if (type == ToPrepareDocument || type == ToPrepareAudio) {
|
||||
mime = mimeType.name();
|
||||
}
|
||||
if (mime == "image/jpeg") {
|
||||
filename = filedialogDefaultName(qsl("image"), qsl(".jpg"), QString(), true);
|
||||
} else if (type == ToPrepareAudio) {
|
||||
filename = filedialogDefaultName(qsl("audio"), qsl(".ogg"), QString(), true);
|
||||
mime = "audio/ogg";
|
||||
} else {
|
||||
QString ext;
|
||||
QStringList patterns = mimeType.globPatterns();
|
||||
if (!patterns.isEmpty()) {
|
||||
ext = patterns.front().replace('*', QString());
|
||||
}
|
||||
filename = filedialogDefaultName(qsl("doc"), ext, QString(), true);
|
||||
filename = filedialogDefaultName((type == ToPrepareAudio) ? qsl("audio") : qsl("doc"), ext, QString(), true);
|
||||
}
|
||||
filesize = data.size();
|
||||
}
|
||||
@@ -136,7 +143,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
}
|
||||
}
|
||||
|
||||
if ((img.isNull() && (type != ToPrepareDocument || !filesize)) || type == ToPrepareAuto || (img.isNull() && file.isEmpty() && data.isEmpty())) { // if could not decide what type
|
||||
if ((img.isNull() && ((type != ToPrepareDocument && type != ToPrepareAudio) || !filesize)) || type == ToPrepareAuto || (img.isNull() && file.isEmpty() && data.isEmpty())) { // if could not decide what type
|
||||
{
|
||||
QMutexLocker lock(loader->toPrepareMutex());
|
||||
ToPrepareMedias &list(loader->toPrepareMedias());
|
||||
@@ -155,6 +162,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
MTPPhotoSize thumb(MTP_photoSizeEmpty(MTP_string("")));
|
||||
MTPPhoto photo(MTP_photoEmpty(MTP_long(0)));
|
||||
MTPDocument document(MTP_documentEmpty(MTP_long(0)));
|
||||
MTPAudio audio(MTP_audioEmpty(MTP_long(0)));
|
||||
|
||||
QByteArray jpeg;
|
||||
if (type == ToPreparePhoto) {
|
||||
@@ -178,7 +186,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
}
|
||||
if (!filesize) filesize = jpeg.size();
|
||||
|
||||
photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
|
||||
thumbId = id;
|
||||
} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) {
|
||||
@@ -188,7 +196,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
if (animated) {
|
||||
attributes.push_back(MTP_documentAttributeAnimated());
|
||||
} else if (mime == stickerMime && w > 0 && h > 0 && w <= StickerMaxSize && h <= StickerMaxSize && filesize < StickerInMemory) {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string("")));
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(""), MTP_inputStickerSetEmpty()));
|
||||
thumbFormat = "webp";
|
||||
thumbExt = qsl("webp");
|
||||
}
|
||||
@@ -210,11 +218,13 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
|
||||
if (type == ToPrepareDocument) {
|
||||
document = MTP_document(MTP_long(id), MTP_long(0), MTP_int(unixtime()), MTP_string(mime), MTP_int(filesize), thumb, MTP_int(MTP::maindc()), MTP_vector<MTPDocumentAttribute>(attributes));
|
||||
} else if (type == ToPrepareAudio) {
|
||||
audio = MTP_audio(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_int(duration), MTP_string(mime), MTP_int(filesize), MTP_int(MTP::maindc()));
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker lock(loader->readyMutex());
|
||||
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
|
||||
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, audio, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -267,6 +277,22 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, MsgI
|
||||
return result;
|
||||
}
|
||||
|
||||
AudioId LocalImageLoader::append(const QByteArray &audio, int32 duration, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
|
||||
AudioId result = 0;
|
||||
{
|
||||
QMutexLocker lock(toPrepareMutex());
|
||||
toPrepare.push_back(ToPrepareMedia(audio, duration, peer, t, false, replyTo));
|
||||
result = toPrepare.back().id;
|
||||
}
|
||||
if (!thread) {
|
||||
thread = new QThread();
|
||||
priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread);
|
||||
thread->start();
|
||||
}
|
||||
emit needToPrepare();
|
||||
return result;
|
||||
}
|
||||
|
||||
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter) {
|
||||
PhotoId result = 0;
|
||||
{
|
||||
|
||||
@@ -20,16 +20,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
enum ToPrepareMediaType {
|
||||
ToPrepareAuto,
|
||||
ToPreparePhoto,
|
||||
ToPrepareAudio,
|
||||
ToPrepareVideo,
|
||||
ToPrepareDocument,
|
||||
};
|
||||
|
||||
struct ToPrepareMedia {
|
||||
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
ToPrepareMedia(const QByteArray &data, int32 duration, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(duration), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
PhotoId id;
|
||||
QString file;
|
||||
@@ -37,6 +40,7 @@ struct ToPrepareMedia {
|
||||
QByteArray data;
|
||||
PeerId peer;
|
||||
ToPrepareMediaType type;
|
||||
int32 duration;
|
||||
bool ctrlShiftEnter;
|
||||
MsgId replyTo;
|
||||
};
|
||||
@@ -44,8 +48,8 @@ typedef QList<ToPrepareMedia> ToPrepareMedias;
|
||||
|
||||
typedef QMap<int32, QByteArray> LocalFileParts;
|
||||
struct ReadyLocalMedia {
|
||||
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
|
||||
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const MTPAudio &audio, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
|
||||
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), audio(audio), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
if (!jpeg.isEmpty()) {
|
||||
int32 size = jpeg.size();
|
||||
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
|
||||
@@ -66,6 +70,7 @@ struct ReadyLocalMedia {
|
||||
|
||||
MTPPhoto photo;
|
||||
MTPDocument document;
|
||||
MTPAudio audio;
|
||||
PreparedPhotoThumbs photoThumbs;
|
||||
LocalFileParts parts;
|
||||
QByteArray jpeg_md5;
|
||||
@@ -107,6 +112,7 @@ public:
|
||||
LocalImageLoader(QObject *parent);
|
||||
void append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
PhotoId append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
AudioId append(const QByteArray &audio, int32 duration, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
PhotoId append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter = false);
|
||||
PhotoId append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "lang.h"
|
||||
|
||||
namespace {
|
||||
enum StickerSetType {
|
||||
StickerSetTypeEmpty = 0,
|
||||
StickerSetTypeID = 1,
|
||||
StickerSetTypeShortName = 2,
|
||||
};
|
||||
|
||||
typedef quint64 FileKey;
|
||||
|
||||
@@ -140,6 +145,10 @@ namespace {
|
||||
return sizeof(quint32) + str.size() * sizeof(ushort);
|
||||
}
|
||||
|
||||
uint32 _bytearraySize(const QByteArray &arr) {
|
||||
return sizeof(quint32) + arr.size();
|
||||
}
|
||||
|
||||
QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted;
|
||||
|
||||
mtpAuthKey _oldKey, _settingsKey, _passKey, _localKey;
|
||||
@@ -484,17 +493,18 @@ namespace {
|
||||
FileKey _dataNameKey = 0;
|
||||
|
||||
enum { // Local Storage Keys
|
||||
lskUserMap = 0,
|
||||
lskDraft, // data: PeerId peer
|
||||
lskDraftPosition, // data: PeerId peer
|
||||
lskImages, // data: StorageKey location
|
||||
lskLocations, // no data
|
||||
lskStickers, // data: StorageKey location
|
||||
lskAudios, // data: StorageKey location
|
||||
lskRecentStickers, // no data
|
||||
lskBackground, // no data
|
||||
lskUserSettings, // no data
|
||||
lskRecentHashtags, // no data
|
||||
lskUserMap = 0x00,
|
||||
lskDraft = 0x01, // data: PeerId peer
|
||||
lskDraftPosition = 0x02, // data: PeerId peer
|
||||
lskImages = 0x03, // data: StorageKey location
|
||||
lskLocations = 0x04, // no data
|
||||
lskStickerImages = 0x05, // data: StorageKey location
|
||||
lskAudios = 0x06, // data: StorageKey location
|
||||
lskRecentStickersOld = 0x07, // no data
|
||||
lskBackground = 0x08, // no data
|
||||
lskUserSettings = 0x09, // no data
|
||||
lskRecentHashtags = 0x0a, // no data
|
||||
lskStickers = 0x0b, // no data
|
||||
};
|
||||
|
||||
typedef QMap<PeerId, FileKey> DraftsMap;
|
||||
@@ -509,7 +519,7 @@ namespace {
|
||||
FileLocationPairs _fileLocationPairs;
|
||||
FileKey _locationsKey = 0;
|
||||
|
||||
FileKey _recentStickersKey = 0;
|
||||
FileKey _recentStickersKeyOld = 0, _stickersKey = 0;
|
||||
|
||||
FileKey _backgroundKey = 0;
|
||||
bool _backgroundWasRead = false;
|
||||
@@ -520,7 +530,7 @@ namespace {
|
||||
|
||||
typedef QPair<FileKey, qint32> FileDesc; // file, size
|
||||
typedef QMap<StorageKey, FileDesc> StorageMap;
|
||||
StorageMap _imagesMap, _stickersMap, _audiosMap;
|
||||
StorageMap _imagesMap, _stickerImagesMap, _audiosMap;
|
||||
int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0;
|
||||
|
||||
bool _mapChanged = false;
|
||||
@@ -585,7 +595,7 @@ namespace {
|
||||
locations.stream >> first >> second >> type >> loc.name >> loc.modified >> loc.size;
|
||||
|
||||
MediaKey key(first, second);
|
||||
loc.type = type;
|
||||
loc.type = StorageFileType(type);
|
||||
|
||||
if (loc.check()) {
|
||||
_fileLocations.insert(key, loc);
|
||||
@@ -831,6 +841,9 @@ namespace {
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetTileBackground(v == 1);
|
||||
if (version < 8005 && !_backgroundKey) {
|
||||
cSetTileBackground(false);
|
||||
}
|
||||
} break;
|
||||
|
||||
case dbiAutoLock: {
|
||||
@@ -902,24 +915,70 @@ namespace {
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
switch (v) {
|
||||
case dbietRecent: cSetEmojiTab(dbietRecent); break;
|
||||
case dbietPeople: cSetEmojiTab(dbietPeople); break;
|
||||
case dbietNature: cSetEmojiTab(dbietNature); break;
|
||||
case dbietObjects: cSetEmojiTab(dbietObjects); break;
|
||||
case dbietPlaces: cSetEmojiTab(dbietPlaces); break;
|
||||
case dbietSymbols: cSetEmojiTab(dbietSymbols); break;
|
||||
case dbietStickers: cSetEmojiTab(dbietStickers); break;
|
||||
case dbietRecent : cSetEmojiTab(dbietRecent ); break;
|
||||
case dbietPeople : cSetEmojiTab(dbietPeople ); break;
|
||||
case dbietNature : cSetEmojiTab(dbietNature ); break;
|
||||
case dbietFood : cSetEmojiTab(dbietFood ); break;
|
||||
case dbietCelebration: cSetEmojiTab(dbietCelebration); break;
|
||||
case dbietActivity : cSetEmojiTab(dbietActivity ); break;
|
||||
case dbietTravel : cSetEmojiTab(dbietTravel ); break;
|
||||
case dbietObjects : cSetEmojiTab(dbietObjects ); break;
|
||||
case dbietStickers : cSetEmojiTab(dbietStickers ); break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case dbiRecentEmojisOld: {
|
||||
RecentEmojisPreloadOld v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
if (!v.isEmpty()) {
|
||||
RecentEmojisPreload p;
|
||||
p.reserve(v.size());
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
uint64 e(v.at(i).first);
|
||||
switch (e) {
|
||||
case 0xD83CDDEFLLU: e = 0xD83CDDEFD83CDDF5LLU; break;
|
||||
case 0xD83CDDF0LLU: e = 0xD83CDDF0D83CDDF7LLU; break;
|
||||
case 0xD83CDDE9LLU: e = 0xD83CDDE9D83CDDEALLU; break;
|
||||
case 0xD83CDDE8LLU: e = 0xD83CDDE8D83CDDF3LLU; break;
|
||||
case 0xD83CDDFALLU: e = 0xD83CDDFAD83CDDF8LLU; break;
|
||||
case 0xD83CDDEBLLU: e = 0xD83CDDEBD83CDDF7LLU; break;
|
||||
case 0xD83CDDEALLU: e = 0xD83CDDEAD83CDDF8LLU; break;
|
||||
case 0xD83CDDEELLU: e = 0xD83CDDEED83CDDF9LLU; break;
|
||||
case 0xD83CDDF7LLU: e = 0xD83CDDF7D83CDDFALLU; break;
|
||||
case 0xD83CDDECLLU: e = 0xD83CDDECD83CDDE7LLU; break;
|
||||
}
|
||||
p.push_back(qMakePair(e, v.at(i).second));
|
||||
}
|
||||
cSetRecentEmojisPreload(p);
|
||||
}
|
||||
} break;
|
||||
|
||||
case dbiRecentEmojis: {
|
||||
RecentEmojiPreload v;
|
||||
RecentEmojisPreload v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetRecentEmojisPreload(v);
|
||||
} break;
|
||||
|
||||
case dbiRecentStickers: {
|
||||
RecentStickerPreload v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetRecentStickersPreload(v);
|
||||
} break;
|
||||
|
||||
case dbiEmojiVariants: {
|
||||
EmojiColorVariants v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetEmojiVariants(v);
|
||||
} break;
|
||||
|
||||
case dbiDialogLastPath: {
|
||||
QString path;
|
||||
stream >> path;
|
||||
@@ -1036,10 +1095,17 @@ namespace {
|
||||
LOG(("App Info: reading old user config.."));
|
||||
qint32 version = 0;
|
||||
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
_dcOpts = &dcOpts;
|
||||
_readOldUserSettingsFields(&file, version);
|
||||
cSetDcOptions(dcOpts);
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
file.close();
|
||||
result = true;
|
||||
@@ -1116,10 +1182,17 @@ namespace {
|
||||
LOG(("App Info: reading old keys.."));
|
||||
qint32 version = 0;
|
||||
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
_dcOpts = &dcOpts;
|
||||
_readOldMtpDataFields(&file, version);
|
||||
cSetDcOptions(dcOpts);
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
file.close();
|
||||
result = true;
|
||||
@@ -1137,7 +1210,9 @@ namespace {
|
||||
|
||||
uint32 size = 11 * (sizeof(quint32) + sizeof(qint32));
|
||||
size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath());
|
||||
size += sizeof(quint32) + sizeof(qint32) + cGetRecentEmojis().size() * (sizeof(uint32) + sizeof(ushort));
|
||||
size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort));
|
||||
size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64));
|
||||
size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort));
|
||||
size += sizeof(quint32) + _stringSize(cDialogLastPath());
|
||||
|
||||
EncryptedDescriptor data(size);
|
||||
@@ -1155,12 +1230,27 @@ namespace {
|
||||
data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab());
|
||||
data.stream << quint32(dbiDialogLastPath) << cDialogLastPath();
|
||||
|
||||
RecentEmojiPreload v;
|
||||
v.reserve(cGetRecentEmojis().size());
|
||||
for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) {
|
||||
v.push_back(qMakePair(i->first->code, i->second));
|
||||
{
|
||||
RecentEmojisPreload v(cRecentEmojisPreload());
|
||||
if (v.isEmpty()) {
|
||||
v.reserve(cGetRecentEmojis().size());
|
||||
for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) {
|
||||
v.push_back(qMakePair(emojiKey(i->first), i->second));
|
||||
}
|
||||
}
|
||||
data.stream << quint32(dbiRecentEmojis) << v;
|
||||
}
|
||||
data.stream << quint32(dbiEmojiVariants) << cEmojiVariants();
|
||||
{
|
||||
RecentStickerPreload v(cRecentStickersPreload());
|
||||
if (v.isEmpty()) {
|
||||
v.reserve(cGetRecentStickers().size());
|
||||
for (RecentStickerPack::const_iterator i = cGetRecentStickers().cbegin(), e = cGetRecentStickers().cend(); i != e; ++i) {
|
||||
v.push_back(qMakePair(i->first->id, i->second));
|
||||
}
|
||||
}
|
||||
data.stream << quint32(dbiRecentStickers) << v;
|
||||
}
|
||||
data.stream << quint32(dbiRecentEmojis) << v;
|
||||
|
||||
FileWriteDescriptor file(_userSettingsKey);
|
||||
file.writeEncrypted(data);
|
||||
@@ -1282,9 +1372,9 @@ namespace {
|
||||
|
||||
DraftsMap draftsMap, draftsPositionsMap;
|
||||
DraftsNotReadMap draftsNotReadMap;
|
||||
StorageMap imagesMap, stickersMap, audiosMap;
|
||||
StorageMap imagesMap, stickerImagesMap, audiosMap;
|
||||
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
|
||||
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
|
||||
quint64 locationsKey = 0, recentStickersKeyOld = 0, stickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
|
||||
while (!map.stream.atEnd()) {
|
||||
quint32 keyType;
|
||||
map.stream >> keyType;
|
||||
@@ -1322,7 +1412,7 @@ namespace {
|
||||
storageImagesSize += size;
|
||||
}
|
||||
} break;
|
||||
case lskStickers: {
|
||||
case lskStickerImages: {
|
||||
quint32 count = 0;
|
||||
map.stream >> count;
|
||||
for (quint32 i = 0; i < count; ++i) {
|
||||
@@ -1330,7 +1420,7 @@ namespace {
|
||||
quint64 first, second;
|
||||
qint32 size;
|
||||
map.stream >> key >> first >> second >> size;
|
||||
stickersMap.insert(StorageKey(first, second), FileDesc(key, size));
|
||||
stickerImagesMap.insert(StorageKey(first, second), FileDesc(key, size));
|
||||
storageStickersSize += size;
|
||||
}
|
||||
} break;
|
||||
@@ -1349,8 +1439,8 @@ namespace {
|
||||
case lskLocations: {
|
||||
map.stream >> locationsKey;
|
||||
} break;
|
||||
case lskRecentStickers: {
|
||||
map.stream >> recentStickersKey;
|
||||
case lskRecentStickersOld: {
|
||||
map.stream >> recentStickersKeyOld;
|
||||
} break;
|
||||
case lskBackground: {
|
||||
map.stream >> backgroundKey;
|
||||
@@ -1361,6 +1451,9 @@ namespace {
|
||||
case lskRecentHashtags: {
|
||||
map.stream >> recentHashtagsKey;
|
||||
} break;
|
||||
case lskStickers: {
|
||||
map.stream >> stickersKey;
|
||||
} break;
|
||||
default:
|
||||
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
|
||||
return Local::ReadMapFailed;
|
||||
@@ -1376,13 +1469,14 @@ namespace {
|
||||
|
||||
_imagesMap = imagesMap;
|
||||
_storageImagesSize = storageImagesSize;
|
||||
_stickersMap = stickersMap;
|
||||
_stickerImagesMap = stickerImagesMap;
|
||||
_storageStickersSize = storageStickersSize;
|
||||
_audiosMap = audiosMap;
|
||||
_storageAudiosSize = storageAudiosSize;
|
||||
|
||||
_locationsKey = locationsKey;
|
||||
_recentStickersKey = recentStickersKey;
|
||||
_recentStickersKeyOld = recentStickersKeyOld;
|
||||
_stickersKey = stickersKey;
|
||||
_backgroundKey = backgroundKey;
|
||||
_userSettingsKey = userSettingsKey;
|
||||
_recentHashtagsKey = recentHashtagsKey;
|
||||
@@ -1442,10 +1536,11 @@ namespace {
|
||||
if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
|
||||
if (!_draftsPositionsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsPositionsMap.size() * sizeof(quint64) * 2;
|
||||
if (!_imagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _imagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (!_stickerImagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickerImagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_stickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
@@ -1468,9 +1563,9 @@ namespace {
|
||||
mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
|
||||
}
|
||||
}
|
||||
if (!_stickersMap.isEmpty()) {
|
||||
mapData.stream << quint32(lskStickers) << quint32(_stickersMap.size());
|
||||
for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
|
||||
if (!_stickerImagesMap.isEmpty()) {
|
||||
mapData.stream << quint32(lskStickerImages) << quint32(_stickerImagesMap.size());
|
||||
for (StorageMap::const_iterator i = _stickerImagesMap.cbegin(), e = _stickerImagesMap.cend(); i != e; ++i) {
|
||||
mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
|
||||
}
|
||||
}
|
||||
@@ -1483,8 +1578,11 @@ namespace {
|
||||
if (_locationsKey) {
|
||||
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
|
||||
}
|
||||
if (_recentStickersKey) {
|
||||
mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
|
||||
if (_recentStickersKeyOld) {
|
||||
mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld);
|
||||
}
|
||||
if (_stickersKey) {
|
||||
mapData.stream << quint32(lskStickers) << quint64(_stickersKey);
|
||||
}
|
||||
if (_backgroundKey) {
|
||||
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey);
|
||||
@@ -1604,7 +1702,11 @@ namespace Local {
|
||||
LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode.."));
|
||||
return writeSettings();
|
||||
}
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
_dcOpts = &dcOpts;
|
||||
LOG(("App Info: reading encrypted settings.."));
|
||||
while (!settings.stream.atEnd()) {
|
||||
@@ -1625,7 +1727,10 @@ namespace Local {
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
|
||||
}
|
||||
}
|
||||
cSetDcOptions(dcOpts);
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
_settingsSalt = salt;
|
||||
}
|
||||
@@ -1646,13 +1751,19 @@ namespace Local {
|
||||
}
|
||||
settings.writeData(_settingsSalt);
|
||||
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
if (dcOpts.isEmpty()) {
|
||||
const BuiltInDc *bdcs = builtInDcs();
|
||||
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
|
||||
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port));
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
|
||||
}
|
||||
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
@@ -1715,9 +1826,9 @@ namespace Local {
|
||||
_draftsPositionsMap.clear();
|
||||
_imagesMap.clear();
|
||||
_draftsNotReadMap.clear();
|
||||
_stickersMap.clear();
|
||||
_stickerImagesMap.clear();
|
||||
_audiosMap.clear();
|
||||
_locationsKey = _recentStickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
|
||||
_locationsKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapNow);
|
||||
|
||||
@@ -1925,13 +2036,13 @@ namespace Local {
|
||||
if (_imagesMap.constFind(location) != _imagesMap.cend()) return;
|
||||
|
||||
QByteArray fmt = image->savedFormat();
|
||||
mtpTypeId format = 0;
|
||||
StorageFileType format = StorageFileUnknown;
|
||||
if (fmt == "JPG") {
|
||||
format = mtpc_storage_fileJpeg;
|
||||
format = StorageFileJpeg;
|
||||
} else if (fmt == "PNG") {
|
||||
format = mtpc_storage_filePng;
|
||||
format = StorageFilePng;
|
||||
} else if (fmt == "GIF") {
|
||||
format = mtpc_storage_fileGif;
|
||||
format = StorageFileGif;
|
||||
}
|
||||
if (format) {
|
||||
image->forget();
|
||||
@@ -1981,7 +2092,7 @@ namespace Local {
|
||||
quint32 imageType;
|
||||
draft.stream >> locFirst >> locSecond >> imageType >> imageData;
|
||||
|
||||
return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(imageType, imageData) : StorageImageSaved();
|
||||
return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(StorageFileType(imageType), imageData) : StorageImageSaved();
|
||||
}
|
||||
|
||||
int32 hasImages() {
|
||||
@@ -1992,13 +2103,13 @@ namespace Local {
|
||||
return _storageImagesSize;
|
||||
}
|
||||
|
||||
void writeSticker(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
|
||||
void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
|
||||
if (!_working()) return;
|
||||
|
||||
qint32 size = _storageStickerSize(sticker.size());
|
||||
StorageMap::const_iterator i = _stickersMap.constFind(location);
|
||||
if (i == _stickersMap.cend()) {
|
||||
i = _stickersMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
StorageMap::const_iterator i = _stickerImagesMap.constFind(location);
|
||||
if (i == _stickerImagesMap.cend()) {
|
||||
i = _stickerImagesMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
_storageStickersSize += size;
|
||||
_mapChanged = true;
|
||||
_writeMap();
|
||||
@@ -2012,20 +2123,20 @@ namespace Local {
|
||||
if (i.value().second != size) {
|
||||
_storageStickersSize += size;
|
||||
_storageStickersSize -= i.value().second;
|
||||
_stickersMap[location].second = size;
|
||||
_stickerImagesMap[location].second = size;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray readSticker(const StorageKey &location) {
|
||||
StorageMap::iterator j = _stickersMap.find(location);
|
||||
if (j == _stickersMap.cend()) {
|
||||
QByteArray readStickerImage(const StorageKey &location) {
|
||||
StorageMap::iterator j = _stickerImagesMap.find(location);
|
||||
if (j == _stickerImagesMap.cend()) {
|
||||
return QByteArray();
|
||||
}
|
||||
FileReadDescriptor draft;
|
||||
if (!readEncryptedFile(draft, j.value().first, UserPath)) {
|
||||
clearKey(j.value().first, UserPath);
|
||||
_storageStickersSize -= j.value().second;
|
||||
_stickersMap.erase(j);
|
||||
_stickerImagesMap.erase(j);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
@@ -2037,7 +2148,7 @@ namespace Local {
|
||||
}
|
||||
|
||||
int32 hasStickers() {
|
||||
return _stickersMap.size();
|
||||
return _stickerImagesMap.size();
|
||||
}
|
||||
|
||||
qint64 storageStickersSize() {
|
||||
@@ -2096,56 +2207,104 @@ namespace Local {
|
||||
return _storageAudiosSize;
|
||||
}
|
||||
|
||||
void writeRecentStickers() {
|
||||
void _writeStickerSet(QDataStream &stream, uint64 setId) {
|
||||
StickerSets::const_iterator it = cStickerSets().constFind(setId);
|
||||
if (it == cStickerSets().cend() || it->stickers.isEmpty()) return;
|
||||
|
||||
stream << quint64(it->id) << quint64(it->access) << it->title << it->shortName << quint32(it->stickers.size());
|
||||
for (StickerPack::const_iterator j = it->stickers.cbegin(), e = it->stickers.cend(); j != e; ++j) {
|
||||
DocumentData *doc = *j;
|
||||
stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->sticker->alt;
|
||||
switch (doc->sticker->set.type()) {
|
||||
case mtpc_inputStickerSetID: {
|
||||
stream << qint32(StickerSetTypeID);
|
||||
} break;
|
||||
case mtpc_inputStickerSetShortName: {
|
||||
stream << qint32(StickerSetTypeShortName);
|
||||
} break;
|
||||
case mtpc_inputStickerSetEmpty:
|
||||
default: {
|
||||
stream << qint32(StickerSetTypeEmpty);
|
||||
} break;
|
||||
}
|
||||
const StorageImageLocation &loc(doc->sticker->loc);
|
||||
stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret);
|
||||
}
|
||||
}
|
||||
|
||||
void writeStickers() {
|
||||
if (!_working()) return;
|
||||
|
||||
const RecentStickerPack &recent(cRecentStickers());
|
||||
if (recent.isEmpty()) {
|
||||
if (_recentStickersKey) {
|
||||
clearKey(_recentStickersKey);
|
||||
_recentStickersKey = 0;
|
||||
|
||||
const StickerSets &sets(cStickerSets());
|
||||
if (sets.isEmpty()) {
|
||||
if (_stickersKey) {
|
||||
clearKey(_stickersKey);
|
||||
_stickersKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
_writeMap();
|
||||
} else {
|
||||
if (!_recentStickersKey) {
|
||||
_recentStickersKey = genKey();
|
||||
if (!_stickersKey) {
|
||||
_stickersKey = genKey();
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = 0;
|
||||
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
|
||||
DocumentData *doc = i->first;
|
||||
if (doc->status == FileFailed) continue;
|
||||
quint32 size = sizeof(quint32) + _bytearraySize(cStickersHash());
|
||||
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
|
||||
if (i->stickers.isEmpty()) continue;
|
||||
|
||||
// id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt
|
||||
size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->alt);
|
||||
// id + access + title + shortName + stickersCount
|
||||
size += sizeof(quint64) * 2 + _stringSize(i->title) + _stringSize(i->shortName) + sizeof(quint32);
|
||||
for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) {
|
||||
DocumentData *doc = *j;
|
||||
|
||||
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set
|
||||
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker->alt) + sizeof(qint32);
|
||||
|
||||
// thumb-width + thumb-height + thumb-dc + thumb-volume + thumb-local + thumb-secret
|
||||
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64);
|
||||
}
|
||||
}
|
||||
EncryptedDescriptor data(size);
|
||||
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
|
||||
DocumentData *doc = i->first;
|
||||
if (doc->status == FileFailed) continue;
|
||||
|
||||
data.stream << quint64(doc->id) << qint16(i->second) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->alt;
|
||||
data.stream << quint32(cStickerSetsOrder().size()) << cStickersHash();
|
||||
_writeStickerSet(data.stream, DefaultStickerSetId);
|
||||
_writeStickerSet(data.stream, CustomStickerSetId);
|
||||
for (StickerSetsOrder::const_iterator i = cStickerSetsOrder().cbegin(), e = cStickerSetsOrder().cend(); i != e; ++i) {
|
||||
_writeStickerSet(data.stream, *i);
|
||||
}
|
||||
FileWriteDescriptor file(_recentStickersKey);
|
||||
FileWriteDescriptor file(_stickersKey);
|
||||
file.writeEncrypted(data);
|
||||
}
|
||||
}
|
||||
|
||||
void readRecentStickers() {
|
||||
if (!_recentStickersKey) return;
|
||||
void importOldRecentStickers() {
|
||||
LOG(("Stickers: importOldStickers()"));
|
||||
if (!_recentStickersKeyOld) return;
|
||||
|
||||
FileReadDescriptor stickers;
|
||||
if (!readEncryptedFile(stickers, _recentStickersKey)) {
|
||||
clearKey(_recentStickersKey);
|
||||
_recentStickersKey = 0;
|
||||
if (!readEncryptedFile(stickers, _recentStickersKeyOld)) {
|
||||
LOG(("Stickers: could not readEncryptedFile(_recentStickersKeyOld)!"));
|
||||
clearKey(_recentStickersKeyOld);
|
||||
_recentStickersKeyOld = 0;
|
||||
_writeMap();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("Stickers: clearing everything in import-old!"));
|
||||
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
sets.clear();
|
||||
cSetStickerSetsOrder(StickerSetsOrder());
|
||||
|
||||
RecentStickerPack &recent(cRefRecentStickers());
|
||||
recent.clear();
|
||||
|
||||
cSetStickersHash(QByteArray());
|
||||
|
||||
StickerSet &def(sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, lang(lng_stickers_default_set), QString())).value());
|
||||
StickerSet &custom(sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString())).value());
|
||||
|
||||
QMap<uint64, bool> read;
|
||||
RecentStickerPack recent;
|
||||
while (!stickers.stream.atEnd()) {
|
||||
quint64 id, access;
|
||||
QString name, mime, alt;
|
||||
@@ -2155,7 +2314,7 @@ namespace Local {
|
||||
if (stickers.version >= 7021) {
|
||||
stickers.stream >> alt;
|
||||
}
|
||||
if (read.contains(id)) continue;
|
||||
if (!value || read.contains(id)) continue;
|
||||
read.insert(id, true);
|
||||
|
||||
QVector<MTPDocumentAttribute> attributes;
|
||||
@@ -2163,23 +2322,134 @@ namespace Local {
|
||||
if (type == AnimatedDocument) {
|
||||
attributes.push_back(MTP_documentAttributeAnimated());
|
||||
} else if (type == StickerDocument) {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt)));
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
|
||||
}
|
||||
if (width > 0 && height > 0) {
|
||||
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
|
||||
}
|
||||
|
||||
recent.push_back(qMakePair(App::document(id, 0, access, date, attributes, mime, ImagePtr(), dc, size), value));
|
||||
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, ImagePtr(), dc, size, StorageImageLocation());
|
||||
if (!doc->sticker) continue;
|
||||
|
||||
if (value > 0) {
|
||||
def.stickers.push_back(doc);
|
||||
} else {
|
||||
custom.stickers.push_back(doc);
|
||||
}
|
||||
if (recent.size() < StickerPanPerRow * StickerPanRowsPerPage && qAbs(value) > 1) recent.push_back(qMakePair(doc, qAbs(value)));
|
||||
}
|
||||
LOG(("Stickers: read %1 default stickers and %2 custom stickers").arg(def.stickers.size()).arg(custom.stickers.size()));
|
||||
if (def.stickers.isEmpty()) sets.remove(DefaultStickerSetId);
|
||||
if (custom.stickers.isEmpty()) sets.remove(CustomStickerSetId);
|
||||
|
||||
writeStickers();
|
||||
writeUserSettings();
|
||||
|
||||
clearKey(_recentStickersKeyOld);
|
||||
_recentStickersKeyOld = 0;
|
||||
_writeMap();
|
||||
LOG(("Stickers: writing stickers and clearing old-stickers"));
|
||||
}
|
||||
|
||||
void readStickers() {
|
||||
LOG(("Stickers: readStickers()"));
|
||||
if (!_stickersKey) {
|
||||
return importOldRecentStickers();
|
||||
}
|
||||
|
||||
cSetRecentStickers(recent);
|
||||
FileReadDescriptor stickers;
|
||||
if (!readEncryptedFile(stickers, _stickersKey)) {
|
||||
LOG(("Stickers: could not readEncryptedFile(_stickersKey)!"));
|
||||
clearKey(_stickersKey);
|
||||
_stickersKey = 0;
|
||||
_writeMap();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("Stickers: clearing everything in read"));
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
sets.clear();
|
||||
|
||||
StickerSetsOrder &order(cRefStickerSetsOrder());
|
||||
order.clear();
|
||||
|
||||
quint32 cnt;
|
||||
QByteArray hash;
|
||||
stickers.stream >> cnt >> hash;
|
||||
for (uint32 i = 0; i < cnt; ++i) {
|
||||
quint64 setId = 0, setAccess = 0;
|
||||
QString setTitle, setShortName;
|
||||
quint32 scnt = 0;
|
||||
stickers.stream >> setId >> setAccess >> setTitle >> setShortName >> scnt;
|
||||
|
||||
if (setId == DefaultStickerSetId) {
|
||||
setTitle = lang(lng_stickers_default_set);
|
||||
} else if (setId == CustomStickerSetId) {
|
||||
setTitle = lang(lng_custom_stickers);
|
||||
} else {
|
||||
order.push_back(setId);
|
||||
}
|
||||
StickerSet &set(sets.insert(setId, StickerSet(setId, setAccess, setTitle, setShortName)).value());
|
||||
set.stickers.reserve(scnt);
|
||||
|
||||
QMap<uint64, bool> read;
|
||||
for (uint32 j = 0; j < scnt; ++j) {
|
||||
quint64 id, access;
|
||||
QString name, mime, alt;
|
||||
qint32 date, dc, size, width, height, type, typeOfSet;
|
||||
stickers.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> alt >> typeOfSet;
|
||||
|
||||
qint32 thumbWidth, thumbHeight, thumbDc, thumbLocal;
|
||||
quint64 thumbVolume, thumbSecret;
|
||||
stickers.stream >> thumbWidth >> thumbHeight >> thumbDc >> thumbVolume >> thumbLocal >> thumbSecret;
|
||||
|
||||
if (read.contains(id)) continue;
|
||||
read.insert(id, true);
|
||||
|
||||
if (setId == DefaultStickerSetId || setId == CustomStickerSetId) {
|
||||
typeOfSet = StickerSetTypeEmpty;
|
||||
}
|
||||
|
||||
QVector<MTPDocumentAttribute> attributes;
|
||||
if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name)));
|
||||
if (type == AnimatedDocument) {
|
||||
attributes.push_back(MTP_documentAttributeAnimated());
|
||||
} else if (type == StickerDocument) {
|
||||
switch (typeOfSet) {
|
||||
case StickerSetTypeID: {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetID(MTP_long(setId), MTP_long(setAccess))));
|
||||
} break;
|
||||
case StickerSetTypeShortName: {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(setShortName))));
|
||||
} break;
|
||||
case StickerSetTypeEmpty:
|
||||
default: {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (width > 0 && height > 0) {
|
||||
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
|
||||
}
|
||||
|
||||
StorageImageLocation thumb(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret);
|
||||
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.dc ? ImagePtr(thumb) : ImagePtr(), dc, size, thumb);
|
||||
if (!doc->sticker) continue;
|
||||
|
||||
set.stickers.push_back(doc);
|
||||
}
|
||||
}
|
||||
|
||||
cSetStickersHash(hash);
|
||||
|
||||
LOG(("Stickers: read %1 sets").arg(sets.size()));
|
||||
}
|
||||
|
||||
void writeBackground(int32 id, const QImage &img) {
|
||||
if (!_working()) return;
|
||||
|
||||
QByteArray png;
|
||||
{
|
||||
if (!img.isNull()) {
|
||||
QBuffer buf(&png);
|
||||
if (!img.save(&buf, "BMP")) return;
|
||||
}
|
||||
@@ -2188,9 +2458,10 @@ namespace Local {
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = sizeof(qint32) + sizeof(quint32) + sizeof(quint32) + png.size();
|
||||
quint32 size = sizeof(qint32) + sizeof(quint32) + (png.isEmpty() ? 0 : (sizeof(quint32) + png.size()));
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << qint32(id) << png;
|
||||
data.stream << qint32(id);
|
||||
if (!png.isEmpty()) data.stream << png;
|
||||
|
||||
FileWriteDescriptor file(_backgroundKey);
|
||||
file.writeEncrypted(data);
|
||||
@@ -2210,7 +2481,17 @@ namespace Local {
|
||||
|
||||
QByteArray pngData;
|
||||
qint32 id;
|
||||
bg.stream >> id >> pngData;
|
||||
bg.stream >> id;
|
||||
if (!id || id == DefaultChatBackground) {
|
||||
if (bg.version < 8005) {
|
||||
if (!id) cSetTileBackground(!DefaultChatBackground);
|
||||
App::initBackground(DefaultChatBackground, QImage(), true);
|
||||
} else {
|
||||
App::initBackground(id, QImage(), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bg.stream >> pngData;
|
||||
|
||||
QImage img;
|
||||
QBuffer buf(&pngData);
|
||||
@@ -2330,8 +2611,8 @@ namespace Local {
|
||||
_storageImagesSize = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (!_stickersMap.isEmpty()) {
|
||||
_stickersMap.clear();
|
||||
if (!_stickerImagesMap.isEmpty()) {
|
||||
_stickerImagesMap.clear();
|
||||
_storageStickersSize = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
@@ -2352,8 +2633,12 @@ namespace Local {
|
||||
_locationsKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_recentStickersKey) {
|
||||
_recentStickersKey = 0;
|
||||
if (_recentStickersKeyOld) {
|
||||
_recentStickersKeyOld = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_stickersKey) {
|
||||
_stickersKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_recentHashtagsKey) {
|
||||
@@ -2380,9 +2665,9 @@ namespace Local {
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (data->stickers.isEmpty()) {
|
||||
data->stickers = _stickersMap;
|
||||
data->stickers = _stickerImagesMap;
|
||||
} else {
|
||||
for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
|
||||
for (StorageMap::const_iterator i = _stickerImagesMap.cbegin(), e = _stickerImagesMap.cend(); i != e; ++i) {
|
||||
StorageKey k = i.key();
|
||||
while (data->stickers.constFind(k) != data->stickers.cend()) {
|
||||
++k.second;
|
||||
@@ -2390,8 +2675,8 @@ namespace Local {
|
||||
data->stickers.insert(k, i.value());
|
||||
}
|
||||
}
|
||||
if (!_stickersMap.isEmpty()) {
|
||||
_stickersMap.clear();
|
||||
if (!_stickerImagesMap.isEmpty()) {
|
||||
_stickerImagesMap.clear();
|
||||
_storageStickersSize = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@ namespace Local {
|
||||
int32 hasImages();
|
||||
qint64 storageImagesSize();
|
||||
|
||||
void writeSticker(const StorageKey &location, const QByteArray &data, bool overwrite = true);
|
||||
QByteArray readSticker(const StorageKey &location);
|
||||
void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true);
|
||||
QByteArray readStickerImage(const StorageKey &location);
|
||||
int32 hasStickers();
|
||||
qint64 storageStickersSize();
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace Local {
|
||||
int32 hasAudios();
|
||||
qint64 storageAudiosSize();
|
||||
|
||||
void writeRecentStickers();
|
||||
void readRecentStickers();
|
||||
void writeStickers();
|
||||
void readStickers();
|
||||
|
||||
void writeBackground(int32 id, const QImage &img);
|
||||
bool readBackground();
|
||||
|
||||
@@ -66,6 +66,7 @@ void debugLogWrite(const char *file, int32 line, const QString &v) {
|
||||
|
||||
{
|
||||
QMutexLocker lock(&debugLogMutex);
|
||||
if (!cDebug() || !debugLogStream) return;
|
||||
|
||||
logsInitDebug(); // maybe need to reopen new file
|
||||
|
||||
@@ -87,6 +88,7 @@ void tcpLogWrite(const QString &v) {
|
||||
|
||||
{
|
||||
QMutexLocker lock(&debugLogMutex);
|
||||
if (!cDebug() || !tcpLogStream) return;
|
||||
|
||||
logsInitDebug(); // maybe need to reopen new file
|
||||
|
||||
@@ -100,6 +102,7 @@ void mtpLogWrite(int32 dc, const QString &v) {
|
||||
|
||||
{
|
||||
QMutexLocker lock(&debugLogMutex);
|
||||
if (!cDebug() || !mtpLogStream) return;
|
||||
|
||||
logsInitDebug(); // maybe need to reopen new file
|
||||
|
||||
@@ -109,7 +112,7 @@ void mtpLogWrite(int32 dc, const QString &v) {
|
||||
}
|
||||
|
||||
void logWrite(const QString &v) {
|
||||
if (!mainLog.isOpen()) return;
|
||||
if (!mainLogStream) return;
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tm;
|
||||
@@ -117,6 +120,8 @@ void logWrite(const QString &v) {
|
||||
|
||||
{
|
||||
QMutexLocker lock(&mainLogMutex);
|
||||
if (!mainLogStream) return;
|
||||
|
||||
QString msg(QString("[%1.%2.%3 %4:%5:%6] %7\n").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero).arg(v));
|
||||
(*mainLogStream) << msg;
|
||||
mainLogStream->flush();
|
||||
|
||||
@@ -26,6 +26,8 @@ int main(int argc, char *argv[]) {
|
||||
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
|
||||
#endif
|
||||
|
||||
InitOpenSSL _init;
|
||||
|
||||
settingsParseArgs(argc, argv);
|
||||
for (int32 i = 0; i < argc; ++i) {
|
||||
if (string("-fixprevious") == argv[i]) {
|
||||
@@ -61,7 +63,12 @@ int main(int argc, char *argv[]) {
|
||||
psStart();
|
||||
int result = 0;
|
||||
{
|
||||
Application app(argc, argv);
|
||||
QByteArray args[] = { "-style=0" }; // prepare fake args
|
||||
static const int a_cnt = sizeof(args) / sizeof(args[0]);
|
||||
int a_argc = a_cnt + 1;
|
||||
char *a_argv[a_cnt + 1] = { argv[0], args[0].data() };
|
||||
|
||||
Application app(a_argc, a_argv);
|
||||
if (!App::quiting()) {
|
||||
result = app.exec();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "settingswidget.h"
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "boxes/stickersetbox.h"
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
@@ -360,7 +361,7 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
|
||||
connect(&dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled()));
|
||||
connect(&history, SIGNAL(cancelled()), &dialogs, SLOT(activate()));
|
||||
connect(this, SIGNAL(peerPhotoChanged(PeerData*)), this, SIGNAL(dialogsUpdated()));
|
||||
connect(&noUpdatesTimer, SIGNAL(timeout()), this, SLOT(getDifference()));
|
||||
connect(&noUpdatesTimer, SIGNAL(timeout()), this, SLOT(mtpPing()));
|
||||
connect(&_onlineTimer, SIGNAL(timeout()), this, SLOT(updateOnline()));
|
||||
connect(&_onlineUpdater, SIGNAL(timeout()), this, SLOT(updateOnlineDisplay()));
|
||||
connect(&_idleFinishTimer, SIGNAL(timeout()), this, SLOT(checkIdleFinish()));
|
||||
@@ -372,10 +373,11 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
|
||||
connect(&history, SIGNAL(peerShown(PeerData*)), this, SLOT(onPeerShown(PeerData*)));
|
||||
connect(&updateNotifySettingTimer, SIGNAL(timeout()), this, SLOT(onUpdateNotifySettings()));
|
||||
connect(this, SIGNAL(showPeerAsync(quint64,qint32,bool,bool)), this, SLOT(showPeer(quint64,qint32,bool,bool)), Qt::QueuedConnection);
|
||||
if (audioVoice()) {
|
||||
connect(audioVoice(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
|
||||
connect(audioVoice(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
|
||||
if (audioPlayer()) {
|
||||
connect(audioPlayer(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
|
||||
connect(audioPlayer(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
|
||||
}
|
||||
connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
|
||||
|
||||
_webPageUpdater.setSingleShot(true);
|
||||
connect(&_webPageUpdater, SIGNAL(timeout()), this, SLOT(webPagesUpdate()));
|
||||
@@ -525,6 +527,9 @@ void MainWidget::webPageUpdated(WebPageData *data) {
|
||||
}
|
||||
|
||||
void MainWidget::webPagesUpdate() {
|
||||
if (_webPagesUpdated.isEmpty()) return;
|
||||
|
||||
_webPageUpdater.stop();
|
||||
const WebPageItems &items(App::webPageItems());
|
||||
for (QMap<WebPageId, bool>::const_iterator i = _webPagesUpdated.cbegin(), e = _webPagesUpdated.cend(); i != e; ++i) {
|
||||
WebPageItems::const_iterator j = items.constFind(App::webPage(i.key()));
|
||||
@@ -538,6 +543,21 @@ void MainWidget::webPagesUpdate() {
|
||||
_webPagesUpdated.clear();
|
||||
}
|
||||
|
||||
void MainWidget::updateMutedIn(int32 seconds) {
|
||||
if (seconds > 86400) seconds = 86400;
|
||||
int32 ms = seconds * 1000;
|
||||
if (_updateMutedTimer.isActive() && _updateMutedTimer.remainingTime() <= ms) return;
|
||||
_updateMutedTimer.start(ms);
|
||||
}
|
||||
|
||||
void MainWidget::updateStickers() {
|
||||
history.updateStickers();
|
||||
}
|
||||
|
||||
void MainWidget::onUpdateMuted() {
|
||||
App::updateMuted();
|
||||
}
|
||||
|
||||
void MainWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||
history.onShareContact(peer, contact);
|
||||
}
|
||||
@@ -705,7 +725,7 @@ void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHis
|
||||
}
|
||||
|
||||
void MainWidget::deleteMessages(const QVector<MTPint> &ids) {
|
||||
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(ids)), rpcDone(&MainWidget::msgsWereDeleted));
|
||||
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(ids)), rpcDone(&MainWidget::messagesAffected));
|
||||
}
|
||||
|
||||
void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) {
|
||||
@@ -962,7 +982,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
|
||||
}
|
||||
}
|
||||
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||
Local::readRecentStickers();
|
||||
Local::readRecentHashtags();
|
||||
recent = cRecentWriteHashtags();
|
||||
}
|
||||
found = true;
|
||||
@@ -993,6 +1013,7 @@ void MainWidget::stopAnimActive() {
|
||||
}
|
||||
|
||||
void MainWidget::searchMessages(const QString &query) {
|
||||
App::wnd()->hideMediaview();
|
||||
dialogs.searchMessages(query);
|
||||
if (!cWideMode()) onShowDialogs();
|
||||
}
|
||||
@@ -1195,7 +1216,8 @@ void MainWidget::peerUsernameChanged(PeerData *peer) {
|
||||
void MainWidget::checkLastUpdate(bool afterSleep) {
|
||||
uint64 n = getms(true);
|
||||
if (_lastUpdateTime && n > _lastUpdateTime + (afterSleep ? NoUpdatesAfterSleepTimeout : NoUpdatesTimeout)) {
|
||||
getDifference();
|
||||
_lastUpdateTime = n;
|
||||
MTP::ping();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1276,7 +1298,7 @@ void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::msgsWereDeleted(const MTPmessages_AffectedMessages &result) {
|
||||
void MainWidget::messagesAffected(const MTPmessages_AffectedMessages &result) {
|
||||
const MTPDmessages_affectedMessages &d(result.c_messages_affectedMessages());
|
||||
updPtsUpdated(d.vpts.v, d.vpts_count.v);
|
||||
}
|
||||
@@ -1335,18 +1357,18 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
|
||||
if (audio->loader) {
|
||||
if (audio->loader->done()) {
|
||||
audio->finish();
|
||||
bool mp3 = (audio->mime == QLatin1String("audio/mp3"));
|
||||
QString already = audio->already();
|
||||
bool play = !mp3 && audio->openOnSave > 0 && audioVoice();
|
||||
bool play = audio->openOnSave > 0 && audioPlayer();
|
||||
if ((!already.isEmpty() && audio->openOnSave) || (!audio->data.isEmpty() && play)) {
|
||||
if (play) {
|
||||
AudioData *playing = 0;
|
||||
VoiceMessageState state = VoiceMessageStopped;
|
||||
audioVoice()->currentState(&playing, &state);
|
||||
if (playing == audio && state != VoiceMessageStopped) {
|
||||
audioVoice()->pauseresume();
|
||||
AudioPlayerState state = AudioPlayerStopped;
|
||||
audioPlayer()->currentState(&playing, &state);
|
||||
if (playing == audio && state != AudioPlayerStopped) {
|
||||
audioPlayer()->pauseresume();
|
||||
} else {
|
||||
audioVoice()->play(audio);
|
||||
audioPlayer()->play(audio);
|
||||
if (App::main()) App::main()->audioMarkRead(audio);
|
||||
}
|
||||
} else {
|
||||
QPoint pos(QCursor::pos());
|
||||
@@ -1355,6 +1377,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
|
||||
} else {
|
||||
psOpenFile(already, audio->openOnSave < 0);
|
||||
}
|
||||
if (App::main()) App::main()->audioMarkRead(audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1369,6 +1392,32 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
|
||||
}
|
||||
|
||||
void MainWidget::audioPlayProgress(AudioData *audio) {
|
||||
AudioData *playing = 0;
|
||||
AudioPlayerState state = AudioPlayerStopped;
|
||||
audioPlayer()->currentState(&playing, &state);
|
||||
if (playing == audio && state == AudioPlayerStoppedAtStart) {
|
||||
audioPlayer()->clearStoppedAtStart(audio);
|
||||
QString already = audio->already(true);
|
||||
if (already.isEmpty() && !audio->data.isEmpty()) {
|
||||
bool mp3 = (audio->mime == QLatin1String("audio/mp3"));
|
||||
QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false);
|
||||
if (!filename.isEmpty()) {
|
||||
QFile f(filename);
|
||||
if (f.open(QIODevice::WriteOnly)) {
|
||||
if (f.write(audio->data) == audio->data.size()) {
|
||||
f.close();
|
||||
already = filename;
|
||||
audio->location = FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename);
|
||||
Local::writeFileLocation(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), audio->dc, audio->id), FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!already.isEmpty()) {
|
||||
psOpenFile(already);
|
||||
}
|
||||
}
|
||||
|
||||
const AudioItems &items(App::audioItems());
|
||||
AudioItems::const_iterator i = items.constFind(audio);
|
||||
if (i != items.cend()) {
|
||||
@@ -1406,8 +1455,10 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
|
||||
HistoryItem *item = App::histItemById(document->openOnSaveMsgId);
|
||||
if (reader.supportsAnimation() && reader.imageCount() > 1 && item) {
|
||||
startGif(item, already);
|
||||
} else if (item) {
|
||||
App::wnd()->showDocument(document, item);
|
||||
} else {
|
||||
App::wnd()->showDocument(document, QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly), item);
|
||||
psOpenFile(already);
|
||||
}
|
||||
} else {
|
||||
psOpenFile(already);
|
||||
@@ -1430,6 +1481,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
|
||||
msgUpdated(j.key()->history()->peer->id, j.key());
|
||||
}
|
||||
}
|
||||
App::wnd()->documentUpdated(document);
|
||||
}
|
||||
|
||||
void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) {
|
||||
@@ -1447,6 +1499,38 @@ void MainWidget::documentLoadRetry() {
|
||||
if (document) document->save(failedFileName);
|
||||
}
|
||||
|
||||
void MainWidget::audioMarkRead(AudioData *data) {
|
||||
const AudioItems &items(App::audioItems());
|
||||
AudioItems::const_iterator i = items.constFind(data);
|
||||
if (i != items.cend()) {
|
||||
mediaMarkRead(i.value());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::videoMarkRead(VideoData *data) {
|
||||
const VideoItems &items(App::videoItems());
|
||||
VideoItems::const_iterator i = items.constFind(data);
|
||||
if (i != items.cend()) {
|
||||
mediaMarkRead(i.value());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::mediaMarkRead(const HistoryItemsMap &items) {
|
||||
QVector<MTPint> markedIds;
|
||||
markedIds.reserve(items.size());
|
||||
for (HistoryItemsMap::const_iterator j = items.cbegin(), e = items.cend(); j != e; ++j) {
|
||||
if (!j.key()->out() && j.key()->isMediaUnread()) {
|
||||
j.key()->markMediaRead();
|
||||
if (j.key()->id > 0) {
|
||||
markedIds.push_back(MTP_int(j.key()->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!markedIds.isEmpty()) {
|
||||
MTP::send(MTPmessages_ReadMessageContents(MTP_vector<MTPint>(markedIds)), rpcDone(&MainWidget::messagesAffected));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::onParentResize(const QSize &newSize) {
|
||||
resize(newSize);
|
||||
}
|
||||
@@ -1590,6 +1674,8 @@ void MainWidget::checkChatBackground() {
|
||||
if (_background->full->loaded()) {
|
||||
if (_background->full->isNull()) {
|
||||
App::initBackground();
|
||||
} else if (_background->id == 0 || _background->id == DefaultChatBackground) {
|
||||
App::initBackground(_background->id);
|
||||
} else {
|
||||
App::initBackground(_background->id, _background->full->pix().toImage());
|
||||
}
|
||||
@@ -2296,6 +2382,7 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
|
||||
|
||||
updInited = true;
|
||||
|
||||
MTP_LOG(0, ("getDifference { good - after a slice of difference was received }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
getDifference();
|
||||
} break;
|
||||
case mtpc_updates_difference: {
|
||||
@@ -2373,6 +2460,7 @@ bool MainWidget::failDifference(const RPCError &error) {
|
||||
void MainWidget::getDifferenceForce() {
|
||||
if (MTP::authedId()) {
|
||||
updInited = true;
|
||||
MTP_LOG(0, ("getDifference { force - after get difference failed }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
getDifference();
|
||||
}
|
||||
}
|
||||
@@ -2398,6 +2486,10 @@ void MainWidget::getDifference() {
|
||||
MTP::send(MTPupdates_GetDifference(MTP_int(updGoodPts), MTP_int(updDate), MTP_int(updQts)), rpcDone(&MainWidget::gotDifference), rpcFail(&MainWidget::failDifference));
|
||||
}
|
||||
|
||||
void MainWidget::mtpPing() {
|
||||
MTP::ping();
|
||||
}
|
||||
|
||||
void MainWidget::start(const MTPUser &user) {
|
||||
int32 uid = user.c_userSelf().vid.v;
|
||||
if (MTP::authedId() != uid) {
|
||||
@@ -2416,7 +2508,7 @@ void MainWidget::start(const MTPUser &user) {
|
||||
}
|
||||
_started = true;
|
||||
App::wnd()->sendServiceHistoryRequest();
|
||||
Local::readRecentStickers();
|
||||
Local::readStickers();
|
||||
history.start();
|
||||
}
|
||||
|
||||
@@ -2425,13 +2517,28 @@ bool MainWidget::started() {
|
||||
}
|
||||
|
||||
void MainWidget::openLocalUrl(const QString &url) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://resolve/?\\?domain=([a-zA-Z0-9\\.\\_]+)$"), QRegularExpression::CaseInsensitiveOption).match(url.trimmed());
|
||||
if (m.hasMatch()) {
|
||||
openUserByName(m.captured(1));
|
||||
QString u(url.trimmed());
|
||||
if (u.startsWith(QLatin1String("tg://resolve"), Qt::CaseInsensitive)) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://resolve/?\\?domain=([a-zA-Z0-9\\.\\_]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
if (m.hasMatch()) {
|
||||
openUserByName(m.captured(1));
|
||||
}
|
||||
} else if (u.startsWith(QLatin1String("tg://join"), Qt::CaseInsensitive)) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
if (m.hasMatch()) {
|
||||
joinGroupByHash(m.captured(1));
|
||||
}
|
||||
} else if (u.startsWith(QLatin1String("tg://addstickers"), Qt::CaseInsensitive)) {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
|
||||
if (m.hasMatch()) {
|
||||
stickersBox(MTP_inputStickerSetShortName(MTP_string(m.captured(1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::openUserByName(const QString &username, bool toProfile) {
|
||||
App::wnd()->hideMediaview();
|
||||
|
||||
UserData *user = App::userByName(username);
|
||||
if (user) {
|
||||
if (toProfile) {
|
||||
@@ -2444,6 +2551,23 @@ void MainWidget::openUserByName(const QString &username, bool toProfile) {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::joinGroupByHash(const QString &hash) {
|
||||
App::wnd()->hideMediaview();
|
||||
MTP::send(MTPmessages_CheckChatInvite(MTP_string(hash)), rpcDone(&MainWidget::inviteCheckDone, hash), rpcFail(&MainWidget::inviteCheckFail));
|
||||
}
|
||||
|
||||
void MainWidget::stickersBox(const MTPInputStickerSet &set) {
|
||||
App::wnd()->hideMediaview();
|
||||
StickerSetBox *box = new StickerSetBox(set);
|
||||
connect(box, SIGNAL(installed(uint64)), this, SLOT(onStickersInstalled(uint64)));
|
||||
App::wnd()->showLayer(box);
|
||||
}
|
||||
|
||||
void MainWidget::onStickersInstalled(uint64 setId) {
|
||||
emit stickersUpdated();
|
||||
history.stickersInstalled(setId);
|
||||
}
|
||||
|
||||
void MainWidget::usernameResolveDone(bool toProfile, const MTPUser &user) {
|
||||
App::wnd()->hideLayer();
|
||||
UserData *u = App::feedUsers(MTP_vector<MTPUser>(1, user));
|
||||
@@ -2463,6 +2587,83 @@ bool MainWidget::usernameResolveFail(QString name, const RPCError &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) {
|
||||
switch (invite.type()) {
|
||||
case mtpc_chatInvite: {
|
||||
const MTPDchatInvite &d(invite.c_chatInvite());
|
||||
ConfirmBox *box = new ConfirmBox(lng_group_invite_want_join(lt_title, qs(d.vtitle)), lang(lng_group_invite_join));
|
||||
_inviteHash = hash;
|
||||
connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport()));
|
||||
App::wnd()->showLayer(box);
|
||||
} break;
|
||||
|
||||
case mtpc_chatInviteAlready: {
|
||||
const MTPDchatInviteAlready &d(invite.c_chatInviteAlready());
|
||||
ChatData *chat = App::feedChats(MTP_vector<MTPChat>(1, d.vchat));
|
||||
if (chat) {
|
||||
if (chat->left) {
|
||||
ConfirmBox *box = new ConfirmBox(lng_group_invite_want_join(lt_title, chat->name), lang(lng_group_invite_join));
|
||||
_inviteHash = '/' + QString::number(chat->id);
|
||||
connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport()));
|
||||
App::wnd()->showLayer(box);
|
||||
} else {
|
||||
showPeer(chat->id, 0, false, true);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWidget::inviteCheckFail(const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
if (error.code() == 400) {
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_group_invite_bad_link), true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWidget::onInviteImport() {
|
||||
if (_inviteHash.isEmpty()) return;
|
||||
if (_inviteHash.at(0) == '/') {
|
||||
PeerId peer = _inviteHash.midRef(1).toULongLong();
|
||||
MTP::send(MTPmessages_AddChatUser(MTP_int(peer & 0xFFFFFFFF), App::self()->inputUser, MTP_int(ForwardOnAdd)), rpcDone(&MainWidget::inviteImportDone), rpcFail(&MainWidget::inviteImportFail), 0, 5);
|
||||
} else {
|
||||
MTP::send(MTPmessages_ImportChatInvite(MTP_string(_inviteHash)), rpcDone(&MainWidget::inviteImportDone), rpcFail(&MainWidget::inviteImportFail));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::inviteImportDone(const MTPUpdates &updates) {
|
||||
App::main()->sentUpdatesReceived(updates);
|
||||
|
||||
App::wnd()->hideLayer();
|
||||
const QVector<MTPChat> *v = 0;
|
||||
switch (updates.type()) {
|
||||
case mtpc_updates: v = &updates.c_updates().vchats.c_vector().v; break;
|
||||
case mtpc_updatesCombined: v = &updates.c_updatesCombined().vchats.c_vector().v; break;
|
||||
case mtpc_updateShort: {
|
||||
} break;
|
||||
case mtpc_updateShortMessage: {
|
||||
} break;
|
||||
case mtpc_updateShortChatMessage: {
|
||||
} break;
|
||||
case mtpc_updatesTooLong: {
|
||||
} break;
|
||||
}
|
||||
if (v && !v->isEmpty() && v->front().type() == mtpc_chat) {
|
||||
App::main()->showPeer(App::peerFromChat(v->front().c_chat().vid.v));
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWidget::inviteImportFail(const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
if (error.code() == 400) {
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(error.type() == qsl("USERS_TOO_MUCH") ? lng_group_invite_no_room : lng_group_invite_bad_link), true), App::wnd()->layerShown());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWidget::startFull(const MTPVector<MTPUser> &users) {
|
||||
const QVector<MTPUser> &v(users.c_vector().v);
|
||||
if (v.isEmpty() || v[0].type() != mtpc_userSelf) { // wtf?..
|
||||
@@ -2485,7 +2686,9 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti
|
||||
delete data->notify;
|
||||
}
|
||||
data->notify = EmptyNotifySettings;
|
||||
App::history(data->id)->setMute(false);
|
||||
App::unregMuted(data);
|
||||
History *h = App::history(data->id);
|
||||
h->setMute(false);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -2493,16 +2696,15 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti
|
||||
case mtpc_peerNotifySettings: {
|
||||
const MTPDpeerNotifySettings &d(settings.c_peerNotifySettings());
|
||||
NotifySettingsPtr setTo = UnknownNotifySettings;
|
||||
PeerId peerId = 0;
|
||||
PeerData *data = 0;
|
||||
switch (peer.type()) {
|
||||
case mtpc_notifyAll: setTo = globalNotifyAllPtr = &globalNotifyAll; break;
|
||||
case mtpc_notifyUsers: setTo = globalNotifyUsersPtr = &globalNotifyUsers; break;
|
||||
case mtpc_notifyChats: setTo = globalNotifyChatsPtr = &globalNotifyChats; break;
|
||||
case mtpc_notifyPeer: {
|
||||
PeerData *data = App::peerLoaded(App::peerFromMTP(peer.c_notifyPeer().vpeer));
|
||||
data = App::peerLoaded(App::peerFromMTP(peer.c_notifyPeer().vpeer));
|
||||
if (!data) break;
|
||||
|
||||
peerId = data->id;
|
||||
if (data->notify == UnknownNotifySettings || data->notify == EmptyNotifySettings) {
|
||||
data->notify = new NotifySettings();
|
||||
}
|
||||
@@ -2515,11 +2717,13 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti
|
||||
setTo->sound = d.vsound.c_string().v;
|
||||
setTo->previews = d.vshow_previews.v;
|
||||
setTo->events = d.vevents_mask.v;
|
||||
if (peerId) {
|
||||
if (!history) history = App::history(peerId);
|
||||
if (isNotifyMuted(setTo)) {
|
||||
if (data) {
|
||||
if (!history) history = App::history(data->id);
|
||||
int32 changeIn = 0;
|
||||
if (isNotifyMuted(setTo, &changeIn)) {
|
||||
App::wnd()->notifyClear(history);
|
||||
history->setMute(true);
|
||||
App::regMuted(data, changeIn);
|
||||
} else {
|
||||
history->setMute(false);
|
||||
}
|
||||
@@ -2560,46 +2764,48 @@ bool MainWidget::failNotifySetting(MTPInputNotifyPeer peer, const RPCError &erro
|
||||
|
||||
void MainWidget::updateNotifySetting(PeerData *peer, bool enabled) {
|
||||
updateNotifySettingPeers.insert(peer);
|
||||
int32 muteFor = 86400 * 365;
|
||||
if (peer->notify == EmptyNotifySettings) {
|
||||
if (!enabled) {
|
||||
peer->notify = new NotifySettings();
|
||||
peer->notify->sound = "";
|
||||
peer->notify->mute = unixtime() + 86400 * 365;
|
||||
peer->notify->mute = unixtime() + muteFor;
|
||||
}
|
||||
} else {
|
||||
if (peer->notify == UnknownNotifySettings) {
|
||||
peer->notify = new NotifySettings();
|
||||
}
|
||||
peer->notify->sound = enabled ? "default" : "";
|
||||
peer->notify->mute = enabled ? 0 : (unixtime() + 86400 * 365);
|
||||
peer->notify->mute = enabled ? 0 : (unixtime() + muteFor);
|
||||
}
|
||||
if (!enabled) {
|
||||
App::regMuted(peer, muteFor + 1);
|
||||
} else {
|
||||
App::unregMuted(peer);
|
||||
}
|
||||
App::history(peer->id)->setMute(!enabled);
|
||||
updateNotifySettingTimer.start(NotifySettingSaveTimeout);
|
||||
}
|
||||
|
||||
void MainWidget::incrementSticker(DocumentData *sticker) {
|
||||
RecentStickerPack recent(cRecentStickers());
|
||||
if (!sticker || !sticker->sticker) return;
|
||||
|
||||
RecentStickerPack &recent(cGetRecentStickers());
|
||||
RecentStickerPack::iterator i = recent.begin(), e = recent.end();
|
||||
for (; i != e; ++i) {
|
||||
if (i->first == sticker) {
|
||||
if (i->second > 0) {
|
||||
++i->second;
|
||||
} else {
|
||||
--i->second;
|
||||
}
|
||||
if (qAbs(i->second) > 0x4000) {
|
||||
++i->second;
|
||||
if (i->second > 0x8000) {
|
||||
for (RecentStickerPack::iterator j = recent.begin(); j != e; ++j) {
|
||||
if (qAbs(j->second) > 1) {
|
||||
if (j->second > 1) {
|
||||
j->second /= 2;
|
||||
} else if (j->second > 0) {
|
||||
j->second = 1;
|
||||
} else {
|
||||
j->second = -1;
|
||||
j->second = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (; i != recent.begin(); --i) {
|
||||
if (qAbs((i - 1)->second) > qAbs(i->second)) {
|
||||
if ((i - 1)->second > i->second) {
|
||||
break;
|
||||
}
|
||||
qSwap(*i, *(i - 1));
|
||||
@@ -2608,11 +2814,45 @@ void MainWidget::incrementSticker(DocumentData *sticker) {
|
||||
}
|
||||
}
|
||||
if (i == e) {
|
||||
recent.push_front(qMakePair(sticker, -(recent.isEmpty() ? 1 : qAbs(recent.front().second))));
|
||||
while (recent.size() >= StickerPanPerRow * StickerPanRowsPerPage) recent.pop_back();
|
||||
recent.push_back(qMakePair(sticker, 1));
|
||||
for (i = recent.end() - 1; i != recent.begin(); --i) {
|
||||
if ((i - 1)->second > i->second) {
|
||||
break;
|
||||
}
|
||||
qSwap(*i, *(i - 1));
|
||||
}
|
||||
}
|
||||
cSetRecentStickers(recent);
|
||||
Local::writeRecentStickers();
|
||||
|
||||
Local::writeUserSettings();
|
||||
|
||||
bool found = false;
|
||||
uint64 setId = 0;
|
||||
QString setName;
|
||||
switch (sticker->sticker->set.type()) {
|
||||
case mtpc_inputStickerSetID: setId = sticker->sticker->set.c_inputStickerSetID().vid.v; break;
|
||||
case mtpc_inputStickerSetShortName: setName = qs(sticker->sticker->set.c_inputStickerSetShortName().vshort_name).toLower().trimmed(); break;
|
||||
}
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
|
||||
if (i->id == CustomStickerSetId || (setId && i->id == setId) || (!setName.isEmpty() && i->shortName.toLower().trimmed() == setName) || (!setId && setName.isEmpty() && i->id == DefaultStickerSetId)) {
|
||||
for (int32 j = 0, l = i->stickers.size(); j < l; ++j) {
|
||||
if (i->stickers.at(j) == sticker) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
StickerSets::iterator it = sets.find(CustomStickerSetId);
|
||||
if (it == sets.cend()) {
|
||||
it = sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString()));
|
||||
}
|
||||
it->stickers.push_back(sticker);
|
||||
Local::writeStickers();
|
||||
}
|
||||
history.updateRecentStickers();
|
||||
}
|
||||
|
||||
@@ -2743,6 +2983,7 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
|
||||
if (mtpTypeId(*from) == mtpc_new_session_created) {
|
||||
MTPNewSession newSession(from, end);
|
||||
updSeq = 0;
|
||||
MTP_LOG(0, ("getDifference { after new_session_created }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
return getDifference();
|
||||
} else {
|
||||
try {
|
||||
@@ -2805,6 +3046,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
|
||||
case mtpc_updateShortMessage: {
|
||||
const MTPDupdateShortMessage &d(updates.c_updateShortMessage());
|
||||
if (!App::userLoaded(d.vuser_id.v) || (d.has_fwd_from_id() && !App::userLoaded(d.vfwd_from_id.v))) {
|
||||
MTP_LOG(0, ("getDifference { good - getting user for updateShortMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
return getDifference();
|
||||
}
|
||||
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) {
|
||||
@@ -2822,7 +3064,10 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
|
||||
|
||||
case mtpc_updateShortChatMessage: {
|
||||
const MTPDupdateShortChatMessage &d(updates.c_updateShortChatMessage());
|
||||
if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v) || (d.has_fwd_from_id() && !App::userLoaded(d.vfwd_from_id.v))) {
|
||||
bool noFrom = !App::userLoaded(d.vfrom_id.v);
|
||||
if (!App::chatLoaded(d.vchat_id.v) || noFrom || (d.has_fwd_from_id() && !App::userLoaded(d.vfwd_from_id.v))) {
|
||||
MTP_LOG(0, ("getDifference { good - getting user for updateShortChatMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
if (noFrom) App::api()->requestFullPeer(App::chatLoaded(d.vchat_id.v));
|
||||
return getDifference();
|
||||
}
|
||||
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) {
|
||||
@@ -2838,6 +3083,7 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
|
||||
} break;
|
||||
|
||||
case mtpc_updatesTooLong: {
|
||||
MTP_LOG(0, ("getDifference { good - updatesTooLong received }%1").arg(cTestMode() ? " TESTMODE" : ""));
|
||||
return getDifference();
|
||||
} break;
|
||||
}
|
||||
@@ -2895,13 +3141,21 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadMessages: {
|
||||
const MTPDupdateReadMessages &d(update.c_updateReadMessages());
|
||||
case mtpc_updateReadMessagesContents: {
|
||||
const MTPDupdateReadMessagesContents &d(update.c_updateReadMessagesContents());
|
||||
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) {
|
||||
_byPtsUpdate.insert(ptsKey(SkippedUpdate), update);
|
||||
return;
|
||||
}
|
||||
App::feedWereRead(d.vmessages.c_vector().v);
|
||||
const QVector<MTPint> &v(d.vmessages.c_vector().v);
|
||||
for (int32 i = 0, l = v.size(); i < l; ++i) {
|
||||
if (HistoryItem *item = App::histItemById(v.at(i).v)) {
|
||||
if (item->isMediaUnread()) {
|
||||
item->markMediaRead();
|
||||
msgUpdated(item->history()->peer->id, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadHistoryInbox: {
|
||||
@@ -2928,6 +3182,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
const MTPDupdateWebPage &d(update.c_updateWebPage());
|
||||
App::feedWebPage(d.vwebpage);
|
||||
history.updatePreview();
|
||||
webPagesUpdate();
|
||||
} break;
|
||||
|
||||
case mtpc_updateDeleteMessages: {
|
||||
|
||||
@@ -185,8 +185,12 @@ public:
|
||||
bool animStep(float64 ms);
|
||||
|
||||
void start(const MTPUser &user);
|
||||
|
||||
void openLocalUrl(const QString &str);
|
||||
void openUserByName(const QString &name, bool toProfile = false);
|
||||
void joinGroupByHash(const QString &hash);
|
||||
void stickersBox(const MTPInputStickerSet &set);
|
||||
|
||||
void startFull(const MTPVector<MTPUser> &users);
|
||||
bool started();
|
||||
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
|
||||
@@ -333,7 +337,14 @@ public:
|
||||
void cancelForwarding();
|
||||
void finishForwarding(History *hist); // send them
|
||||
|
||||
void audioMarkRead(AudioData *data);
|
||||
void videoMarkRead(VideoData *data);
|
||||
void mediaMarkRead(const HistoryItemsMap &items);
|
||||
|
||||
void webPageUpdated(WebPageData *page);
|
||||
void updateMutedIn(int32 seconds);
|
||||
|
||||
void updateStickers();
|
||||
|
||||
~MainWidget();
|
||||
|
||||
@@ -346,6 +357,7 @@ signals:
|
||||
void dialogToTop(const History::DialogLinks &links);
|
||||
void dialogsUpdated();
|
||||
void showPeerAsync(quint64 peer, qint32 msgId, bool back, bool force);
|
||||
void stickersUpdated();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -367,6 +379,7 @@ public slots:
|
||||
|
||||
void onParentResize(const QSize &newSize);
|
||||
void getDifference();
|
||||
void mtpPing();
|
||||
void getDifferenceForce();
|
||||
|
||||
void updateOnline(bool gotOtherOffline = false);
|
||||
@@ -391,10 +404,16 @@ public slots:
|
||||
|
||||
void onCacheBackground();
|
||||
|
||||
void onInviteImport();
|
||||
|
||||
void onUpdateMuted();
|
||||
|
||||
void onStickersInstalled(uint64 setId);
|
||||
|
||||
private:
|
||||
|
||||
void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result);
|
||||
void msgsWereDeleted(const MTPmessages_AffectedMessages &result);
|
||||
void messagesAffected(const MTPmessages_AffectedMessages &result);
|
||||
void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req);
|
||||
|
||||
bool _started;
|
||||
@@ -412,6 +431,8 @@ private:
|
||||
QMap<WebPageId, bool> _webPagesUpdated;
|
||||
QTimer _webPageUpdater;
|
||||
|
||||
SingleTimer _updateMutedTimer;
|
||||
|
||||
void gotDifference(const MTPupdates_Difference &diff);
|
||||
bool failDifference(const RPCError &e);
|
||||
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);
|
||||
@@ -429,6 +450,12 @@ private:
|
||||
void usernameResolveDone(bool toProfile, const MTPUser &user);
|
||||
bool usernameResolveFail(QString name, const RPCError &error);
|
||||
|
||||
void inviteCheckDone(QString hash, const MTPChatInvite &invite);
|
||||
bool inviteCheckFail(const RPCError &error);
|
||||
QString _inviteHash;
|
||||
void inviteImportDone(const MTPUpdates &result);
|
||||
bool inviteImportFail(const RPCError &error);
|
||||
|
||||
void hideAll();
|
||||
void showAll();
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dropdown.h"
|
||||
|
||||
class MediaView : public TWidget, public RPCSender, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -37,30 +39,48 @@ public:
|
||||
|
||||
void hide();
|
||||
|
||||
void updateOver(const QPoint &mpos);
|
||||
void updateOver(QPoint mpos);
|
||||
|
||||
void showPhoto(PhotoData *photo, HistoryItem *context);
|
||||
void showPhoto(PhotoData *photo, PeerData *context);
|
||||
void showDocument(DocumentData *doc, QPixmap pix, HistoryItem *context);
|
||||
void showDocument(DocumentData *doc, HistoryItem *context);
|
||||
void moveToScreen();
|
||||
void moveToPhoto(int32 delta);
|
||||
void preloadPhotos(int32 delta);
|
||||
void moveToNext(int32 delta);
|
||||
void preloadData(int32 delta);
|
||||
|
||||
void leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget
|
||||
updateOverState(OverNone);
|
||||
}
|
||||
void enterFromChildEvent(QEvent *e) { // e -- from leaveEvent() of child TWidget
|
||||
updateOver(mapFromGlobal(QCursor::pos()));
|
||||
}
|
||||
|
||||
void mediaOverviewUpdated(PeerData *peer);
|
||||
void documentUpdated(DocumentData *doc);
|
||||
void changingMsgId(HistoryItem *row, MsgId newId);
|
||||
void updateDocSize();
|
||||
void updateControls();
|
||||
void updateDropdown();
|
||||
|
||||
bool animStep(float64 dt);
|
||||
|
||||
void showSaveMsgFile();
|
||||
void close();
|
||||
|
||||
void activateControls();
|
||||
void onDocClick();
|
||||
|
||||
~MediaView();
|
||||
|
||||
public slots:
|
||||
|
||||
void onClose();
|
||||
void onSave();
|
||||
void onHideControls(bool force = false);
|
||||
void onDropdownHiding();
|
||||
|
||||
void onToMessage();
|
||||
void onSaveAs();
|
||||
void onDownload();
|
||||
void onSaveCancel();
|
||||
void onShowInFolder();
|
||||
void onForward();
|
||||
void onDelete();
|
||||
@@ -69,52 +89,76 @@ public slots:
|
||||
void onMenuDestroy(QObject *obj);
|
||||
void receiveMouse();
|
||||
|
||||
void onDropdown();
|
||||
|
||||
void onCheckActive();
|
||||
void onTouchTimer();
|
||||
|
||||
void updateImage();
|
||||
void onGifUpdated();
|
||||
|
||||
private:
|
||||
|
||||
void showPhoto(PhotoData *photo);
|
||||
void loadPhotosBack();
|
||||
void displayPhoto(PhotoData *photo, HistoryItem *item);
|
||||
void displayDocument(DocumentData *doc, HistoryItem *item);
|
||||
void findCurrent();
|
||||
void loadBack();
|
||||
|
||||
void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req);
|
||||
void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req);
|
||||
void filesLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req);
|
||||
|
||||
void updateHeader();
|
||||
void updatePolaroid();
|
||||
void snapXY();
|
||||
|
||||
QBrush _transparentBrush;
|
||||
|
||||
QTimer _timer;
|
||||
PhotoData *_photo;
|
||||
DocumentData *_doc;
|
||||
QRect _avail, _leftNav, _rightNav, _bottomBar, _nameNav, _dateNav, _polaroidOut, _polaroidIn;
|
||||
int32 _availBottom;
|
||||
bool _leftNavVisible, _rightNavVisible;
|
||||
MediaOverviewType _overview;
|
||||
QRect _closeNav, _closeNavIcon;
|
||||
QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon;
|
||||
QRect _headerNav, _nameNav, _dateNav;
|
||||
QRect _saveNav, _saveNavIcon, _moreNav, _moreNavIcon;
|
||||
bool _leftNavVisible, _rightNavVisible, _saveVisible, _headerHasLink;
|
||||
QString _dateText;
|
||||
QString _headerText;
|
||||
|
||||
Text _caption;
|
||||
QRect _captionRect;
|
||||
|
||||
uint64 _animStarted;
|
||||
|
||||
int32 _maxWidth, _maxHeight, _width, _x, _y, _w, _h, _xStart, _yStart;
|
||||
int32 _width, _x, _y, _w, _h, _xStart, _yStart;
|
||||
int32 _zoom; // < 0 - out, 0 - none, > 0 - in
|
||||
float64 _zoomToScreen; // for documents
|
||||
QPoint _mStart;
|
||||
bool _pressed;
|
||||
int32 _dragging;
|
||||
QPixmap _current;
|
||||
AnimatedGif _currentGif;
|
||||
int32 _full; // -1 - thumb, 0 - medium, 1 - full
|
||||
|
||||
History *_history; // if conversation photos overview
|
||||
PeerData *_peer;
|
||||
UserData *_user, *_from; // if user profile photos overview
|
||||
Text _fromName;
|
||||
int32 _index; // index in photos array, -1 if just photo
|
||||
MsgId _msgid; // msgId of current photo
|
||||
style::sprite _docIcon;
|
||||
QString _docName, _docSize, _docExt;
|
||||
int32 _docNameWidth, _docSizeWidth, _docExtWidth;
|
||||
QRect _docRect, _docIconRect;
|
||||
int32 _docThumbx, _docThumby, _docThumbw;
|
||||
uint64 _docRadialFirst, _docRadialStart, _docRadialLast;
|
||||
float64 _docRadialOpacity;
|
||||
QPen _docRadialPen;
|
||||
anim::fvalue a_docRadial, a_docRadialStart;
|
||||
LinkButton _docDownload, _docSaveAs, _docCancel;
|
||||
|
||||
QString _header;
|
||||
History *_history; // if conversation photos or files overview
|
||||
PeerData *_peer;
|
||||
UserData *_user; // if user profile photos overview
|
||||
|
||||
UserData *_from;
|
||||
Text _fromName;
|
||||
|
||||
int32 _index; // index in photos or files array, -1 if just photo
|
||||
MsgId _msgid; // msgId of current photo or file
|
||||
|
||||
mtpRequestId _loadRequest;
|
||||
|
||||
@@ -122,14 +166,34 @@ private:
|
||||
OverNone,
|
||||
OverLeftNav,
|
||||
OverRightNav,
|
||||
OverClose,
|
||||
OverHeader,
|
||||
OverName,
|
||||
OverDate
|
||||
OverDate,
|
||||
OverSave,
|
||||
OverMore,
|
||||
OverIcon,
|
||||
};
|
||||
OverState _over, _down;
|
||||
QPoint _lastAction;
|
||||
QPoint _lastAction, _lastMouseMovePos;
|
||||
bool _ignoringDropdown;
|
||||
|
||||
enum ControlsState {
|
||||
ControlsShowing,
|
||||
ControlsShown,
|
||||
ControlsHiding,
|
||||
ControlsHidden,
|
||||
};
|
||||
ControlsState _controlsState;
|
||||
uint64 _controlsAnimStarted;
|
||||
QTimer _controlsHideTimer;
|
||||
anim::fvalue a_cOpacity;
|
||||
|
||||
IconedButton _close, _save, _forward, _delete, _overview;
|
||||
ContextMenu *_menu;
|
||||
Dropdown _dropdown;
|
||||
IconedButton *_btnSaveCancel, *_btnToMessage, *_btnShowInFolder, *_btnSaveAs, *_btnCopy, *_btnForward, *_btnDelete, *_btnViewAll;
|
||||
QList<IconedButton*> _btns;
|
||||
|
||||
bool _receiveMouse;
|
||||
|
||||
bool _touchPress, _touchMove, _touchRightButton;
|
||||
@@ -149,6 +213,7 @@ private:
|
||||
typedef QMap<OverState, anim::fvalue> ShowingOpacities;
|
||||
ShowingOpacities _animOpacities;
|
||||
|
||||
void updateOverRect(OverState state);
|
||||
bool updateOverState(OverState newState);
|
||||
float64 overLevel(OverState control);
|
||||
QColor overColor(const QColor &a, float64 ca, const QColor &b, float64 cb);
|
||||
|
||||
@@ -117,6 +117,7 @@ with open('scheme.tl') as f:
|
||||
prms = {};
|
||||
conditions = {};
|
||||
prmsList = [];
|
||||
conditionsList = [];
|
||||
isTemplate = hasFlags = hasTemplate = '';
|
||||
for param in paramsList:
|
||||
if (re.match(r'^\s*$', param)):
|
||||
@@ -149,7 +150,9 @@ with open('scheme.tl') as f:
|
||||
print('Bad param found: "' + param + '" in line: ' + line);
|
||||
continue;
|
||||
ptype = pmasktype.group(3);
|
||||
conditions[pname] = pmasktype.group(2);
|
||||
if (not pname in conditions):
|
||||
conditionsList.append(pname);
|
||||
conditions[pname] = pmasktype.group(2);
|
||||
elif (ptype.find('<') >= 0):
|
||||
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype);
|
||||
if (templ):
|
||||
@@ -198,11 +201,11 @@ with open('scheme.tl') as f:
|
||||
if (len(conditions)):
|
||||
funcsText += '\n';
|
||||
funcsText += '\tenum {\n';
|
||||
for paramName in conditions.keys():
|
||||
for paramName in conditionsList:
|
||||
funcsText += '\t\tflag_' + paramName + ' = (1 << ' + conditions[paramName] + '),\n';
|
||||
funcsText += '\t};\n';
|
||||
funcsText += '\n';
|
||||
for paramName in conditions.keys():
|
||||
for paramName in conditionsList:
|
||||
funcsText += '\tbool has_' + paramName + '() const { return v' + hasFlags + '.v & flag_' + paramName + '; }\n';
|
||||
|
||||
funcsText += '\n';
|
||||
@@ -210,7 +213,7 @@ with open('scheme.tl') as f:
|
||||
size = [];
|
||||
for k in prmsList:
|
||||
v = prms[k];
|
||||
if (k in conditions.keys()):
|
||||
if (k in conditionsList):
|
||||
size.append('(has_' + k + '() ? v' + k + '.innerLength() : 0)');
|
||||
else:
|
||||
size.append('v' + k + '.innerLength()');
|
||||
@@ -224,7 +227,7 @@ with open('scheme.tl') as f:
|
||||
funcsText += '\tvoid read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ') {\n'; # read method
|
||||
for k in prmsList:
|
||||
v = prms[k];
|
||||
if (k in conditions.keys()):
|
||||
if (k in conditionsList):
|
||||
funcsText += '\t\tif (has_' + k + '()) { v' + k + '.read(from, end); } else { v' + k + ' = MTP' + v + '(); }\n';
|
||||
else:
|
||||
funcsText += '\t\tv' + k + '.read(from, end);\n';
|
||||
@@ -233,7 +236,7 @@ with open('scheme.tl') as f:
|
||||
funcsText += '\tvoid write(mtpBuffer &to) const {\n'; # write method
|
||||
for k in prmsList:
|
||||
v = prms[k];
|
||||
if (k in conditions.keys()):
|
||||
if (k in conditionsList):
|
||||
funcsText += '\t\tif (has_' + k + '()) v' + k + '.write(to);\n';
|
||||
else:
|
||||
funcsText += '\t\tv' + k + '.write(to);\n';
|
||||
@@ -269,7 +272,7 @@ with open('scheme.tl') as f:
|
||||
funcsList.append(restype);
|
||||
funcsDict[restype] = [];
|
||||
# TypesDict[restype] = resType;
|
||||
funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditions]);
|
||||
funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions]);
|
||||
else:
|
||||
if (isTemplate != ''):
|
||||
print('Template types not allowed: "' + resType + '" in line: ' + line);
|
||||
@@ -278,7 +281,7 @@ with open('scheme.tl') as f:
|
||||
typesList.append(restype);
|
||||
typesDict[restype] = [];
|
||||
TypesDict[restype] = resType;
|
||||
typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditions]);
|
||||
typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions]);
|
||||
|
||||
consts = consts + 1;
|
||||
|
||||
@@ -292,7 +295,8 @@ def addTextSerialize(lst, dct, dataLetter):
|
||||
prmsList = data[2];
|
||||
prms = data[3];
|
||||
hasFlags = data[4];
|
||||
conditions = data[5];
|
||||
conditionsList = data[5];
|
||||
conditions = data[6];
|
||||
|
||||
if len(result):
|
||||
result += '\n';
|
||||
@@ -311,7 +315,7 @@ def addTextSerialize(lst, dct, dataLetter):
|
||||
result += '\t\t\t\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); ';
|
||||
if (k == hasFlags):
|
||||
result += 'if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; ';
|
||||
if (k in conditions.keys()):
|
||||
if (k in conditionsList):
|
||||
result += 'if (flag & MTP' + dataLetter + name + '::flag_' + k + ') { ';
|
||||
result += 'types.push_back(';
|
||||
vtypeget = re.match(r'^[Vv]ector<MTP([A-Za-z0-9\._]+)>', v);
|
||||
@@ -355,7 +359,7 @@ def addTextSerialize(lst, dct, dataLetter):
|
||||
else:
|
||||
result += '0); vtypes.push_back(0';
|
||||
result += '); stages.push_back(0); flags.push_back(0); ';
|
||||
if (k in conditions.keys()):
|
||||
if (k in conditionsList):
|
||||
result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } ';
|
||||
result += 'break;\n';
|
||||
stage = stage + 1;
|
||||
@@ -396,7 +400,8 @@ for restype in typesList:
|
||||
prmsList = data[2];
|
||||
prms = data[3];
|
||||
hasFlags = data[4];
|
||||
conditions = data[5];
|
||||
conditionsList = data[5];
|
||||
conditions = data[6];
|
||||
|
||||
dataText = '';
|
||||
dataText += '\nclass MTPD' + name + ' : public mtpDataImpl<MTPD' + name + '> {\n'; # data class
|
||||
@@ -451,7 +456,7 @@ for restype in typesList:
|
||||
if (withType):
|
||||
readText += '\t\t';
|
||||
writeText += '\t\t';
|
||||
if (paramName in conditions.keys()):
|
||||
if (paramName in conditionsList):
|
||||
readText += '\tif (v.has_' + paramName + '()) { v.v' + paramName + '.read(from, end); } else { v.v' + paramName + ' = MTP' + paramType + '(); }\n';
|
||||
writeText += '\tif (v.has_' + paramName + '()) v.v' + paramName + '.write(to);\n';
|
||||
sizeList.append('(v.has_' + paramName + '() ? v.v' + paramName + '.innerLength() : 0)');
|
||||
@@ -482,11 +487,11 @@ for restype in typesList:
|
||||
if (len(conditions)):
|
||||
dataText += '\n';
|
||||
dataText += '\tenum {\n';
|
||||
for paramName in conditions.keys():
|
||||
for paramName in conditionsList:
|
||||
dataText += '\t\tflag_' + paramName + ' = (1 << ' + conditions[paramName] + '),\n';
|
||||
dataText += '\t};\n';
|
||||
dataText += '\n';
|
||||
for paramName in conditions.keys():
|
||||
for paramName in conditionsList:
|
||||
dataText += '\tbool has_' + paramName + '() const { return v' + hasFlags + '.v & flag_' + paramName + '; }\n';
|
||||
dataText += '};\n'; # class ending
|
||||
|
||||
|
||||
@@ -23,13 +23,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
namespace {
|
||||
typedef QMap<int32, MTProtoSessionPtr> Sessions;
|
||||
Sessions sessions;
|
||||
QVector<MTProtoSessionPtr> sessionsToKill;
|
||||
MTProtoSessionPtr mainSession;
|
||||
|
||||
typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dc for request to this dc or -dc for request to main dc
|
||||
typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dcWithShift for request to this dc or -dc for request to main dc
|
||||
RequestsByDC requestsByDC;
|
||||
QMutex requestByDCLock;
|
||||
|
||||
typedef QMap<mtpRequestId, int32> AuthExportRequests; // holds target dc for auth export request
|
||||
typedef QMap<mtpRequestId, int32> AuthExportRequests; // holds target dcWithShift for auth export request
|
||||
AuthExportRequests authExportRequests;
|
||||
|
||||
bool _started = false;
|
||||
@@ -55,7 +56,7 @@ namespace {
|
||||
BadGuestDCRequests badGuestDCRequests;
|
||||
|
||||
typedef QVector<mtpRequestId> DCAuthWaiters;
|
||||
typedef QMap<int32, DCAuthWaiters> AuthWaiters;
|
||||
typedef QMap<int32, DCAuthWaiters> AuthWaiters; // holds request ids waiting for auth import to specific dc
|
||||
AuthWaiters authWaiters;
|
||||
|
||||
QMutex toClearLock;
|
||||
@@ -76,12 +77,11 @@ namespace {
|
||||
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
|
||||
return;
|
||||
}
|
||||
int32 newdc = i.value();
|
||||
int32 newdc = i.value() % _mtp_internal::dcShift;
|
||||
|
||||
DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
|
||||
|
||||
DCAuthWaiters &waiters(authWaiters[newdc]);
|
||||
MTProtoSessionPtr session(_mtp_internal::getSession(newdc));
|
||||
if (waiters.size()) {
|
||||
QReadLocker locker(&requestMapLock);
|
||||
for (DCAuthWaiters::iterator i = waiters.begin(), e = waiters.end(); i != e; ++i) {
|
||||
@@ -91,6 +91,7 @@ namespace {
|
||||
LOG(("MTP Error: could not find request %1 for resending").arg(requestId));
|
||||
continue;
|
||||
}
|
||||
int32 dcWithShift = newdc;
|
||||
{
|
||||
RequestsByDC::iterator k = requestsByDC.find(requestId);
|
||||
if (k == requestsByDC.cend()) {
|
||||
@@ -101,11 +102,15 @@ namespace {
|
||||
MTP::setdc(newdc);
|
||||
k.value() = -newdc;
|
||||
} else {
|
||||
k.value() = k.value() - (k.value() % _mtp_internal::dcShift) + newdc;
|
||||
int32 shift = k.value() - (k.value() % _mtp_internal::dcShift);
|
||||
dcWithShift += shift;
|
||||
k.value() = dcWithShift;
|
||||
}
|
||||
DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(requestId).arg(k.value()));
|
||||
}
|
||||
session->sendPrepared(j.value());
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift)) {
|
||||
session->sendPrepared(j.value());
|
||||
}
|
||||
}
|
||||
waiters.clear();
|
||||
}
|
||||
@@ -121,8 +126,8 @@ namespace {
|
||||
void exportDone(const MTPauth_ExportedAuthorization &result, mtpRequestId req) {
|
||||
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
|
||||
if (i == authExportRequests.cend()) {
|
||||
LOG(("MTP Error: auth export request target dc not found, requestId: %1").arg(req));
|
||||
RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dc, request %1").arg(req)));
|
||||
LOG(("MTP Error: auth export request target dcWithShift not found, requestId: %1").arg(req));
|
||||
RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dcWithShift, request %1").arg(req)));
|
||||
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
|
||||
return;
|
||||
}
|
||||
@@ -137,7 +142,7 @@ namespace {
|
||||
|
||||
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
|
||||
if (i != authExportRequests.cend()) {
|
||||
authWaiters[i.value()].clear();
|
||||
authWaiters[i.value() % _mtp_internal::dcShift].clear();
|
||||
}
|
||||
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
|
||||
return true;
|
||||
@@ -151,31 +156,34 @@ namespace {
|
||||
if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) {
|
||||
if (!requestId) return false;
|
||||
|
||||
int32 dc = 0, newdc = m.captured(2).toInt();
|
||||
int32 dcWithShift = 0, newdcWithShift = m.captured(2).toInt();
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i == requestsByDC.end()) {
|
||||
LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdc));
|
||||
LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdcWithShift));
|
||||
} else {
|
||||
dc = i.value();
|
||||
dcWithShift = i.value();
|
||||
}
|
||||
}
|
||||
if (!dc || !newdc) return false;
|
||||
if (!dcWithShift || !newdcWithShift) return false;
|
||||
|
||||
DEBUG_LOG(("MTP Info: changing request %1 dc%2 to %3").arg(requestId).arg((dc > 0) ? "" : " and main dc").arg(newdc));
|
||||
if (dc < 0) {
|
||||
if (MTP::authedId() && !authExportRequests.contains(requestId)) { // import auth, set dc and resend
|
||||
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdc));
|
||||
DCAuthWaiters &waiters(authWaiters[newdc]);
|
||||
DEBUG_LOG(("MTP Info: changing request %1 from dcWithShift%2 to dc%3").arg(requestId).arg(dcWithShift).arg(newdcWithShift));
|
||||
if (dcWithShift < 0) { // newdc shift = 0
|
||||
if (false && MTP::authedId() && !authExportRequests.contains(requestId)) { // migrate not supported at this moment
|
||||
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdcWithShift));
|
||||
DCAuthWaiters &waiters(authWaiters[newdcWithShift]);
|
||||
if (!waiters.size()) {
|
||||
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc);
|
||||
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdcWithShift)), rpcDone(exportDone), rpcFail(exportFail)), newdcWithShift);
|
||||
}
|
||||
waiters.push_back(requestId);
|
||||
return true;
|
||||
} else {
|
||||
MTP::setdc(newdc);
|
||||
MTP::setdc(newdcWithShift);
|
||||
}
|
||||
} else {
|
||||
int32 shift = dcWithShift - (dcWithShift % _mtp_internal::dcShift);
|
||||
newdcWithShift += shift;
|
||||
}
|
||||
|
||||
mtpRequest req;
|
||||
@@ -188,8 +196,10 @@ namespace {
|
||||
}
|
||||
req = i.value();
|
||||
}
|
||||
_mtp_internal::registerRequest(requestId, (dc < 0) ? -newdc : newdc);
|
||||
_mtp_internal::getSession(newdc)->sendPrepared(req);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(newdcWithShift)) {
|
||||
_mtp_internal::registerRequest(requestId, (dcWithShift < 0) ? -newdcWithShift : newdcWithShift);
|
||||
session->sendPrepared(req);
|
||||
}
|
||||
return true;
|
||||
} else if (code < 0 || code >= 500 || (m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) {
|
||||
if (!requestId) return false;
|
||||
@@ -218,26 +228,26 @@ namespace {
|
||||
|
||||
return true;
|
||||
} else if (code == 401 || (badGuestDC && badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend())) {
|
||||
int32 dc = 0;
|
||||
int32 dcWithShift = 0;
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i != requestsByDC.end()) {
|
||||
dc = i.value();
|
||||
dcWithShift = i.value();
|
||||
} else {
|
||||
LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
|
||||
}
|
||||
}
|
||||
int32 newdc = abs(dc) % _mtp_internal::dcShift;
|
||||
int32 newdc = abs(dcWithShift) % _mtp_internal::dcShift;
|
||||
if (!newdc || newdc == mtpMainDC() || !MTP::authedId()) {
|
||||
if (!badGuestDC && globalHandler.onFail) (*globalHandler.onFail)(requestId, error); // auth failed in main dc
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(dc));
|
||||
DEBUG_LOG(("MTP Info: importing auth to dcWithShift %1").arg(dcWithShift));
|
||||
DCAuthWaiters &waiters(authWaiters[newdc]);
|
||||
if (!waiters.size()) {
|
||||
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc);
|
||||
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), abs(dcWithShift));
|
||||
}
|
||||
waiters.push_back(requestId);
|
||||
if (badGuestDC) badGuestDCRequests.insert(requestId);
|
||||
@@ -253,20 +263,22 @@ namespace {
|
||||
}
|
||||
req = i.value();
|
||||
}
|
||||
int32 dc = 0;
|
||||
int32 dcWithShift = 0;
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i == requestsByDC.end()) {
|
||||
LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId));
|
||||
} else {
|
||||
dc = i.value();
|
||||
dcWithShift = i.value();
|
||||
}
|
||||
}
|
||||
if (!dc) return false;
|
||||
if (!dcWithShift) return false;
|
||||
|
||||
req->needsLayer = true;
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
|
||||
req->needsLayer = true;
|
||||
session->sendPrepared(req);
|
||||
}
|
||||
return true;
|
||||
} else if (err == qsl("MSG_WAIT_FAILED")) {
|
||||
mtpRequest req;
|
||||
@@ -283,7 +295,7 @@ namespace {
|
||||
LOG(("MTP Error: wait failed for not dependent request %1").arg(requestId));
|
||||
return false;
|
||||
}
|
||||
int32 dc = 0;
|
||||
int32 dcWithShift = 0;
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId), j = requestsByDC.find(req->after->requestId);
|
||||
@@ -292,19 +304,21 @@ namespace {
|
||||
} else if (j == requestsByDC.end()) {
|
||||
LOG(("MTP Error: could not find dependent request %1 by dc").arg(req->after->requestId));
|
||||
} else {
|
||||
dc = i.value();
|
||||
dcWithShift = i.value();
|
||||
if (i.value() != j.value()) {
|
||||
req->after = mtpRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dc) return false;
|
||||
if (!dcWithShift) return false;
|
||||
|
||||
if (!req->after) {
|
||||
req->needsLayer = true;
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
|
||||
req->needsLayer = true;
|
||||
session->sendPrepared(req);
|
||||
}
|
||||
} else {
|
||||
int32 newdc = abs(dc) % _mtp_internal::dcShift;
|
||||
int32 newdc = abs(dcWithShift) % _mtp_internal::dcShift;
|
||||
DCAuthWaiters &waiters(authWaiters[newdc]);
|
||||
if (waiters.indexOf(req->after->requestId) >= 0) {
|
||||
if (waiters.indexOf(requestId) < 0) {
|
||||
@@ -338,27 +352,27 @@ namespace {
|
||||
}
|
||||
|
||||
namespace _mtp_internal {
|
||||
MTProtoSessionPtr getSession(int32 dc) {
|
||||
MTProtoSessionPtr getSession(int32 dcWithShift) {
|
||||
if (!_started) return MTProtoSessionPtr();
|
||||
if (!dc) return mainSession;
|
||||
if (!(dc % _mtp_internal::dcShift)) {
|
||||
dc += mainSession->getDC();
|
||||
if (!dcWithShift) return mainSession;
|
||||
if (!(dcWithShift % _mtp_internal::dcShift)) {
|
||||
dcWithShift += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
|
||||
}
|
||||
|
||||
Sessions::const_iterator i = sessions.constFind(dc);
|
||||
Sessions::const_iterator i = sessions.constFind(dcWithShift);
|
||||
if (i != sessions.cend()) return *i;
|
||||
|
||||
MTProtoSessionPtr result(new MTProtoSession());
|
||||
result->start(dc);
|
||||
result->start(dcWithShift);
|
||||
|
||||
sessions.insert(dc, result);
|
||||
sessions.insert(dcWithShift, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void registerRequest(mtpRequestId requestId, int32 dc) {
|
||||
void registerRequest(mtpRequestId requestId, int32 dcWithShift) {
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
requestsByDC.insert(requestId, dc);
|
||||
requestsByDC.insert(requestId, dcWithShift);
|
||||
}
|
||||
_mtp_internal::performDelayedClear(); // need to do it somewhere..
|
||||
}
|
||||
@@ -530,12 +544,12 @@ namespace _mtp_internal {
|
||||
if (globalHandler.onDone) (*globalHandler.onDone)(0, from, end); // some updates were received
|
||||
}
|
||||
|
||||
void onStateChange(int32 dc, int32 state) {
|
||||
if (stateChangedHandler) stateChangedHandler(dc, state);
|
||||
void onStateChange(int32 dcWithShift, int32 state) {
|
||||
if (stateChangedHandler) stateChangedHandler(dcWithShift, state);
|
||||
}
|
||||
|
||||
void onSessionReset(int32 dc) {
|
||||
if (sessionResetHandler) sessionResetHandler(dc);
|
||||
void onSessionReset(int32 dcWithShift) {
|
||||
if (sessionResetHandler) sessionResetHandler(dcWithShift);
|
||||
}
|
||||
|
||||
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data
|
||||
@@ -561,12 +575,12 @@ namespace _mtp_internal {
|
||||
mtpRequestId requestId = delayedRequests.front().first;
|
||||
delayedRequests.pop_front();
|
||||
|
||||
int32 dc = 0;
|
||||
int32 dcWithShift = 0;
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::const_iterator i = requestsByDC.constFind(requestId);
|
||||
if (i != requestsByDC.cend()) {
|
||||
dc = i.value();
|
||||
dcWithShift = i.value();
|
||||
} else {
|
||||
LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId));
|
||||
continue;
|
||||
@@ -583,7 +597,9 @@ namespace _mtp_internal {
|
||||
}
|
||||
req = j.value();
|
||||
}
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
|
||||
session->sendPrepared(req);
|
||||
}
|
||||
}
|
||||
|
||||
if (!delayedRequests.isEmpty()) {
|
||||
@@ -595,13 +611,15 @@ namespace _mtp_internal {
|
||||
namespace MTP {
|
||||
|
||||
void start() {
|
||||
if (started()) return;
|
||||
|
||||
unixtimeInit();
|
||||
|
||||
MTProtoDCMap &dcs(mtpDCMap());
|
||||
|
||||
mainSession = MTProtoSessionPtr(new MTProtoSession());
|
||||
mainSession->start(mtpMainDC());
|
||||
sessions[mainSession->getDC()] = mainSession;
|
||||
sessions[mainSession->getDcWithShift()] = mainSession;
|
||||
|
||||
_started = true;
|
||||
resender = new _mtp_internal::RequestResender();
|
||||
@@ -625,8 +643,9 @@ namespace MTP {
|
||||
void restart(int32 dcMask) {
|
||||
if (!_started) return;
|
||||
|
||||
dcMask %= _mtp_internal::dcShift;
|
||||
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
|
||||
if ((*i)->getDC() % _mtp_internal::dcShift == dcMask % _mtp_internal::dcShift) {
|
||||
if (((*i)->getDcWithShift() % int(_mtp_internal::dcShift)) == dcMask) {
|
||||
(*i)->restart();
|
||||
}
|
||||
}
|
||||
@@ -641,8 +660,9 @@ namespace MTP {
|
||||
void setdc(int32 dc, bool fromZeroOnly) {
|
||||
if (!dc || !_started) return;
|
||||
mtpSetDC(dc, fromZeroOnly);
|
||||
if (maindc() != mainSession->getDC()) {
|
||||
mainSession = _mtp_internal::getSession(maindc());
|
||||
int32 oldMainDc = mainSession->getDcWithShift();
|
||||
if (maindc() != oldMainDc) {
|
||||
killSession(oldMainDc);
|
||||
}
|
||||
Local::writeMtpData();
|
||||
}
|
||||
@@ -656,7 +676,7 @@ namespace MTP {
|
||||
|
||||
if (!dc) return mainSession->getState();
|
||||
if (!(dc % _mtp_internal::dcShift)) {
|
||||
dc += mainSession->getDC();
|
||||
dc += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
|
||||
}
|
||||
|
||||
Sessions::const_iterator i = sessions.constFind(dc);
|
||||
@@ -670,7 +690,7 @@ namespace MTP {
|
||||
|
||||
if (!dc) return mainSession->transport();
|
||||
if (!(dc % _mtp_internal::dcShift)) {
|
||||
dc += mainSession->getDC();
|
||||
dc += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
|
||||
}
|
||||
|
||||
Sessions::const_iterator i = sessions.constFind(dc);
|
||||
@@ -679,9 +699,10 @@ namespace MTP {
|
||||
return QString();
|
||||
}
|
||||
|
||||
void initdc(int32 dc) {
|
||||
if (!_started) return;
|
||||
_mtp_internal::getSession(dc);
|
||||
void ping() {
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(0)) {
|
||||
session->ping();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel(mtpRequestId requestId) {
|
||||
@@ -699,25 +720,35 @@ namespace MTP {
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i != requestsByDC.end()) {
|
||||
_mtp_internal::getSession(abs(i.value()))->cancel(requestId, msgId);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) {
|
||||
session->cancel(requestId, msgId);
|
||||
}
|
||||
requestsByDC.erase(i);
|
||||
}
|
||||
}
|
||||
_mtp_internal::clearCallbacks(requestId);
|
||||
}
|
||||
|
||||
void killSessionsDelayed() {
|
||||
if (!sessionsToKill.isEmpty()) {
|
||||
sessionsToKill.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void killSession(int32 dc) {
|
||||
Sessions::iterator i = sessions.find(dc);
|
||||
if (i != sessions.end()) {
|
||||
bool wasMain = (i.value() == mainSession);
|
||||
|
||||
i.value()->stop();
|
||||
i.value()->kill();
|
||||
if (sessionsToKill.isEmpty()) QTimer::singleShot(0, killSessionsDelayed);
|
||||
sessionsToKill.push_back(i.value());
|
||||
sessions.erase(i);
|
||||
|
||||
if (wasMain) {
|
||||
mainSession = MTProtoSessionPtr(new MTProtoSession());
|
||||
mainSession->start(mtpMainDC());
|
||||
sessions[mainSession->getDC()] = mainSession;
|
||||
sessions[mainSession->getDcWithShift()] = mainSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -736,22 +767,30 @@ namespace MTP {
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i != requestsByDC.end()) {
|
||||
return _mtp_internal::getSession(abs(i.value()))->requestState(requestId);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) {
|
||||
return session->requestState(requestId);
|
||||
}
|
||||
return MTP::RequestConnecting;
|
||||
}
|
||||
return MTP::RequestSent;
|
||||
}
|
||||
return _mtp_internal::getSession(-requestId)->requestState(0);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(-requestId)) {
|
||||
return session->requestState(0);
|
||||
}
|
||||
return MTP::RequestConnecting;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) {
|
||||
i.value()->stop();
|
||||
i.value()->kill();
|
||||
}
|
||||
sessions.clear();
|
||||
mainSession = MTProtoSessionPtr();
|
||||
delete resender;
|
||||
resender = 0;
|
||||
mtpDestroyConfigLoader();
|
||||
|
||||
_started = false;
|
||||
}
|
||||
|
||||
void authed(int32 uid) {
|
||||
@@ -803,4 +842,8 @@ namespace MTP {
|
||||
return mtpSetKey(dc, key);
|
||||
}
|
||||
|
||||
QReadWriteLock *dcOptionsMutex() {
|
||||
return mtpDcOptionsMutex();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace _mtp_internal {
|
||||
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end);
|
||||
bool hasCallbacks(mtpRequestId requestId);
|
||||
void globalCallback(const mtpPrime *from, const mtpPrime *end);
|
||||
void onStateChange(int32 dc, int32 state);
|
||||
void onSessionReset(int32 dc);
|
||||
void onStateChange(int32 dcWithShift, int32 state);
|
||||
void onSessionReset(int32 dcWithShift);
|
||||
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); // return true if need to clean request data
|
||||
inline bool rpcErrorOccured(mtpRequestId requestId, const RPCResponseHandler &handler, const RPCError &err) {
|
||||
return rpcErrorOccured(requestId, handler.onFail, err);
|
||||
@@ -64,6 +64,7 @@ namespace _mtp_internal {
|
||||
namespace MTP {
|
||||
|
||||
static const uint32 cfg = 1 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum
|
||||
static const uint32 lgt = 2 * _mtp_internal::dcShift; // send(MTPauth_LogOut(), MTP::lgt + dc) - for logout of guest dcs enum
|
||||
static const uint32 dld[MTPDownloadSessionsCount] = { // send(req, callbacks, MTP::dld[i] + dc) - for download
|
||||
0x10 * _mtp_internal::dcShift,
|
||||
0x11 * _mtp_internal::dcShift,
|
||||
@@ -89,18 +90,19 @@ namespace MTP {
|
||||
|
||||
int32 dcstate(int32 dc = 0);
|
||||
QString dctransport(int32 dc = 0);
|
||||
void initdc(int32 dc);
|
||||
|
||||
template <typename TRequest>
|
||||
inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
|
||||
MTProtoSessionPtr session = _mtp_internal::getSession(dc);
|
||||
if (!session) return 0;
|
||||
|
||||
return session->send(request, callbacks, msCanWait, true, !dc, after);
|
||||
if (MTProtoSessionPtr session = _mtp_internal::getSession(dc)) {
|
||||
return session->send(request, callbacks, msCanWait, true, !dc, after);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
template <typename TRequest>
|
||||
inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
|
||||
return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait, after);
|
||||
}
|
||||
void ping();
|
||||
void cancel(mtpRequestId req);
|
||||
void killSession(int32 dc);
|
||||
void stopSession(int32 dc);
|
||||
@@ -138,6 +140,8 @@ namespace MTP {
|
||||
mtpKeysMap getKeys();
|
||||
void setKey(int32 dc, mtpAuthKeyPtr key);
|
||||
|
||||
QReadWriteLock *dcOptionsMutex();
|
||||
|
||||
};
|
||||
|
||||
#include "mtproto/mtpSessionImpl.h"
|
||||
|
||||