Compare commits

...

75 Commits

Author SHA1 Message Date
John Preston
f35853c42e version 0.8.15.dev ready 2015-05-20 22:54:15 +03:00
John Preston
44492b9e2d fixed round corners for forward bubble and for retina 2015-05-20 22:50:05 +03:00
John Preston
a46bb46e54 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-20 22:29:08 +03:00
John Preston
84c190c293 langs updated 2015-05-20 22:28:52 +03:00
John Preston
9da4a21f94 caption display in video, caption display in mediaview, removed Loading / Failed webpage display, rounded corners everywhere 2015-05-20 22:28:24 +03:00
John Preston
2a3a351445 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-20 00:20:14 +03:00
John Preston
928cd0a8cb Great Minds went to langpack 2015-05-20 00:19:11 +03:00
John Preston
509d2189c1 added stickersetbox to .pro file 2015-05-19 19:02:41 +03:00
John Preston
725fa87188 removing new added stickers from custom 2015-05-19 18:58:22 +03:00
John Preston
147eaab59a prepared version 0.8.14.dev 2015-05-19 18:48:00 +03:00
John Preston
890ec34202 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-19 18:46:51 +03:00
John Preston
92858dc7d3 sticker packs done 2015-05-19 18:46:45 +03:00
John Preston
4440b42848 openssl multithread locks support added 2015-05-19 18:42:32 +03:00
John Preston
136fd5c8e1 added lock for dcOptions, emojibox large emoji display, session management improved, new emoji dropdown started 2015-05-14 19:50:04 +03:00
John Preston
d92356ce28 improved emoji text replaces 2015-05-12 18:01:49 +03:00
John Preston
47f673aa69 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-12 17:25:56 +03:00
John Preston
64d4a3e8a8 removed QT_STYLE_OVERRIDE env variable read 2015-05-12 15:03:10 +03:00
John Preston
87e72fc7aa langs updated 2015-05-12 14:53:50 +03:00
John Preston
6d44a3ec95 emoji sprites improved 2015-05-11 16:33:02 +03:00
John Preston
eb47eabba4 langs updated 2015-05-11 16:31:37 +03:00
John Preston
cfb0de69f0 next layer, stickerdata moved from documentdata 2015-05-11 15:44:27 +03:00
John Preston
2d46cc4c11 preparing version 0.8.13 with new emoji pan 2015-05-11 13:18:57 +03:00
John Preston
9761c5bb56 fixed 0.8.12.dev version 2015-05-08 19:01:11 +03:00
John Preston
7d6bf487a7 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-08 17:44:40 +03:00
John Preston
6fda783fc1 version 0.8.12.dev with new emojis support 2015-05-08 17:44:07 +03:00
John Preston
aeaa039542 cmd+backspace support added 2015-05-08 17:36:05 +03:00
John Preston
62e85b1cf0 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-08 16:08:36 +03:00
John Preston
080a08fa76 some optimizations in shortchatmessage 2015-05-08 16:06:53 +03:00
John Preston
be1eb1c693 Merge branch 'emojis' into dev 2015-05-08 16:02:33 +03:00
John Preston
daf5d4acdb removed temp code 2015-05-08 16:02:24 +03:00
John Preston
f4781b9117 recent emojis more frequent update 2015-05-08 15:58:34 +03:00
John Preston
0f778431f5 new emoji support done 2015-05-08 15:45:14 +03:00
John Preston
2121ce1210 version 0.8.11 stable is prepared 2015-05-01 12:14:43 +03:00
John Preston
c54aadcac3 version 0.8.10.dev with fixes in webpage layout, history load and history msgs reg 2015-05-01 02:05:19 +03:00
John Preston
3fc74166de langs updated 2015-05-01 02:03:22 +03:00
John Preston
749b13adec dev version 0.8.9 fixed telegram.me joinchat 2015-04-30 18:11:21 +03:00
John Preston
c33ddf49ff langs updated 2015-04-30 18:09:06 +03:00
John Preston
bc1e2dcb54 langs updated 2015-04-30 16:58:21 +03:00
John Preston
9a193ed88f new emojis rendered 2015-04-30 16:56:33 +03:00
John Preston
fb32c5bcd1 version 0.8.8.dev - audio listened status, photo caption display, map places name-address display, gray muted badges, invite links support, some fixes 2015-04-30 16:53:36 +03:00
John Preston
67b46d9aac Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into emojis 2015-04-25 16:55:12 +03:00
John Preston
aeb2ec68ef added some debug info in autoupdate, version 0.8.7 stable prepared 2015-04-24 19:02:45 +03:00
John Preston
e1c304c2e5 langs updated, qt multimedia dependency removed from xcode project 2015-04-24 18:59:02 +03:00
John Preston
c7de9d4668 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-23 22:58:41 +03:00
John Preston
45aa6dff75 langs updated 2015-04-23 22:58:35 +03:00
John Preston
8b0562b946 lang updated 2015-04-23 22:58:02 +03:00
John Preston
373f1a0ff0 added old chat bg to git and to telegram_linux.qrc 2015-04-23 19:03:31 +03:00
John Preston
058f6bd8de Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-23 19:02:11 +03:00
John Preston
a707f7b9e7 0.8.6 prepared - forwarded info display in media (except photos and stickers), grouped forwarded notification, old bg returned, critical mtproto fix 2015-04-23 19:01:43 +03:00
John Preston
61d1574023 added forwarded and from names in files / audios / videos / contacts display, grouped notification for many forwarded messages, fix of getDifference() for new_session_created 2015-04-23 18:50:11 +03:00
John Preston
fe59898e5c changed chat bg in linux resource file 2015-04-22 15:05:29 +03:00
John Preston
0df1952a04 changelog added 2015-04-22 14:33:52 +03:00
John Preston
1d8ec7c7d6 dev-version 0.8.5 prepared - new photoviewer, new default chat background 2015-04-22 14:21:45 +03:00
John Preston
b770ea4f8d optimized new mediaview, os x new mediaview done 2015-04-19 22:01:45 +03:00
John Preston
b31f2d952c regen emojis started 2015-04-19 14:16:48 +03:00
John Preston
370c47d95b new photoviewer adapted for documents 2015-04-19 13:29:19 +03:00
John Preston
9b3767e77c Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-16 18:00:43 +03:00
John Preston
196b643e7d Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-16 18:00:16 +03:00
John Preston
c9626c140c sending ping_delay_disconnect instead of getDifference by no-updates timeouts 2015-04-16 17:59:42 +03:00
John Preston
a356a4dc06 dont cache default background image, cmd+m minimizes window in os x 2015-04-16 14:14:25 +03:00
John Preston
34d5c3777b Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-04-15 11:07:27 +02:00
John Preston
ac74a08d53 fixed crash in photo update from 2015-04-15 09:23:39 +02:00
John Preston
47b0f901c8 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-04-13 10:59:32 +01:00
John Preston
18103aae75 xcode project updated 2015-04-13 10:59:28 +01:00
John Preston
cc9ae13297 fixed vimeo links preview 2015-04-13 10:58:30 +01:00
John Preston
334b3ac706 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-13 10:41:01 +01:00
John Preston
b8f19786a1 0.8.4 changelog 2015-04-13 10:05:47 +01:00
John Preston
a9063eb87b Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-13 09:58:34 +01:00
John Preston
175e0f71ce fixed reply to text color 2015-04-13 09:58:13 +01:00
John Preston
51f9e8f681 links preview resize fixed, version 0.8.4 2015-04-13 09:54:06 +01:00
John Preston
782c254ea0 started photoviewer redesign 2015-04-11 11:04:10 +01:00
John Preston
8bc0410830 version 0.8.3 prepared 2015-04-08 17:22:06 +03:00
John Preston
70f3568e16 version 0.8.2.dev ready 2015-04-08 02:03:32 +03:00
John Preston
fb2c140fad langs updated 2015-04-08 01:48:35 +03:00
John Preston
9ede565a00 webPage links preview previews done 2015-04-07 01:15:29 +03:00
142 changed files with 16208 additions and 10317 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,9 +1,9 @@
@echo OFF
set "AppVersion=8001"
set "AppVersionStrSmall=0.8.1"
set "AppVersionStr=0.8.1"
set "AppVersionStrFull=0.8.1.0"
set "AppVersion=8015"
set "AppVersionStrSmall=0.8.15"
set "AppVersionStr=0.8.15"
set "AppVersionStrFull=0.8.15.0"
set "DevChannel=1"
if %DevChannel% neq 0 goto preparedev

View File

@@ -261,6 +261,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minute|# minutes}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# hour|# hours}";
"lng_passcode_enter_old" = "Enter old passcode";
"lng_passcode_enter_first" = "Enter a passcode";
"lng_passcode_enter_new" = "Enter new passcode";
"lng_passcode_confirm_new" = "Re-enter new passcode";
"lng_passcode_about" = "When a local passcode is set, a lock icon appears in the top menu. Click it to lock the app.\n\nNote: if you forget your local passcode, you'll need to relogin in Telegram Desktop.";
@@ -278,6 +279,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_cloud_password_set" = "Enable two-step verification";
"lng_cloud_password_edit" = "Change cloud password";
"lng_cloud_password_enter_old" = "Enter old password";
"lng_cloud_password_enter_first" = "Enter a password";
"lng_cloud_password_enter_new" = "Enter new password";
"lng_cloud_password_confirm_new" = "Re-enter new password";
"lng_cloud_password_hint" = "Enter password hint";
@@ -322,11 +324,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_later" = "Later";
"lng_sessions_header" = "Current session";
"lng_sessions_other_header" = "Other sessions";
"lng_sessions_other_header" = "Active sessions";
"lng_sessions_no_other" = "No other sessions";
"lng_sessions_other_desc" = "You can log in to Telegram from other\nmobile, tablet and desktop devices, using\nthe same phone number. All your data\nwill be instantly synchronized.";
"lng_sessions_terminate_all" = "Terminate all";
"lng_preview_loading" = "Getting Link Info..";
"lng_profile_chat_unaccessible" = "Group is unaccessible";
"lng_topbar_info" = "Info";
"lng_profile_settings_section" = "Settings";
@@ -378,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";
@@ -403,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";
@@ -454,6 +491,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";
@@ -523,12 +561,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}";
@@ -541,7 +584,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";

View File

@@ -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;
@@ -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;
@@ -699,6 +699,7 @@ dlgDateSkip: 5px;
dlgUnreadColor: #FFF;
dlgUnreadBG: #6fc766;
dlgUnreadMutedBG: #bbb;
dlgUnreadFont: font(12px bold);
dlgUnreadPaddingHor: 5px;
dlgUnreadPaddingVer: 1px;
@@ -773,6 +774,8 @@ historyToEndSkip: 10px;
activeFadeInDuration: 500;
activeFadeOutDuration: 3000;
msgRadius: 3px;
msgMaxWidth: 550px;
msgFont: font(fsize);
msgNameFont: font(fsize semibold);
@@ -787,12 +790,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 +819,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 +831,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 +850,7 @@ msgImgDblCheckRect: sprite(300px, 65px, 20px, 20px);
msgDateImgDelta: 4px;
msgDateImgColor: #fff;
msgDateImgBg: #00000054;
msgDateImgSelectBg: #1c4a7187;
msgDateImgPadding: point(8px, 2px);
msgDateImgCheckSpace: 4px;
@@ -860,23 +862,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 +920,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,21 +984,21 @@ 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;
}
replySkip: 52px;
replySkip: 51px;
replyColor: #377aae;
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 +1009,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 +1021,8 @@ historyScroll: flatScroll(scrollDef) {
width: 12px;
deltax: 3px;
deltay: 3px;
deltat: 3px;
deltab: 3px;
topsh: 0px;
bottomsh: -1px;
@@ -1168,10 +1177,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 +1363,22 @@ 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);
duration: 150;
width: 0px;
}
dropdownAttachDocument: iconedButton(btnAttachDocument) {
iconPos: point(14px, 13px);
@@ -1441,41 +1453,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: 180px;
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(105px, 196px, 21px, 22px);
emojiActivityOver: sprite(308px, 264px, 21px, 22px);
emojiActivityActive: sprite(308px, 286px, 21px, 22px);
emojiTravel: sprite(126px, 196px, 21px, 22px);
emojiTravelOver: sprite(321px, 344px, 21px, 22px);
emojiTravelActive: sprite(321px, 366px, 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 +1546,7 @@ rbEmoji: flatCheckbox {
cursor: cursor(pointer);
disabledCursor: cursor(default);
imagePos: point(5px, 8px);
imagePos: point(8px, 12px);
}
rbEmojiRecent: flatCheckbox(rbEmoji) {
imageRect: emojiRecent;
@@ -1511,6 +1572,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 +1612,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 +1788,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: 0.7;
overviewLoader: size(34px, 14px);
overviewLoaderPoint: size(4px, 4px);
overviewLoaderSkip: 4px;
@@ -1773,8 +1867,8 @@ mentionFont: linkFont;
mentionPhotoSize: msgPhotoSize;
sessionsHeight: 440px;
sessionHeight: 50px;
sessionPadding: margins(20px, 7px, 20px, 0);
sessionHeight: 70px;
sessionPadding: margins(20px, 10px, 20px, 0);
sessionsCloseButton: flatButton(aboutCloseButton) {
width: boxWidth;
}
@@ -1783,7 +1877,7 @@ sessionActiveFont: msgDateFont;
sessionActiveColor: #aaa;
sessionInfoFont: msgFont;
sessionInfoColor: dlgTextColor;
sessionTerminateTop: 17px;
sessionTerminateTop: 30px;
sessionTerminateSkip: 10px;
sessionTerminate: iconedButton(notifyClose) {
iconPos: point(3px, 3px);

View File

@@ -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,14 @@ switcher {
duration: number;
}
dropdown {
border: number;
borderColor: color;
padding: margins;
shadow: sprite;
duration: number;
width: number;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);
@@ -262,7 +265,7 @@ void ApiWrap::gotWebPages(const MTPmessages_Messages &msgs, mtpRequestId req) {
}
const WebPageItems &items(App::webPageItems());
for (WebPagesPending::iterator i = _webPagesPending.begin(); i != _webPagesPending.cend(); ++i) {
for (WebPagesPending::iterator i = _webPagesPending.begin(); i != _webPagesPending.cend();) {
if (i.value() == req) {
if (i.key()->pendingTill > 0) {
i.key()->pendingTill = -1;

View File

@@ -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);
@@ -747,19 +782,6 @@ namespace App {
}
}
void feedMessageMedia(MsgId msgId, const MTPMessage &msg) {
const MTPMessageMedia *media = 0;
switch (msg.type()) {
case mtpc_message: media = &msg.c_message().vmedia; break;
}
if (media) {
MsgsData::iterator i = msgsData.find(msgId);
if (i != msgsData.cend()) {
i.value()->updateMedia(*media);
}
}
}
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert) {
switch (photo.type()) {
case mtpc_photo: {
@@ -882,7 +904,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);
}
@@ -895,18 +917,18 @@ 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) {
return App::webPage(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), webpage.has_site_name() ? qs(webpage.vsite_name) : QString(), webpage.has_title() ? qs(webpage.vtitle) : QString(), webpage.has_description() ? qs(webpage.vdescription) : QString(), webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : 0, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString());
return App::webPage(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), webpage.has_site_name() ? qs(webpage.vsite_name) : QString(), webpage.has_title() ? qs(webpage.vtitle) : QString(), webpage.has_description() ? qs(webpage.vdescription) : QString(), webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : 0, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString(), 0);
}
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert) {
@@ -916,7 +938,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;
@@ -1136,7 +1162,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);
@@ -1154,12 +1188,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);
@@ -1169,6 +1219,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 {
@@ -1183,19 +1234,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;
}
}
}
}
@@ -1211,7 +1266,7 @@ namespace App {
}
convert->id = webPage;
}
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill)) {
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) {
convert->type = toWebPageType(type);
convert->url = url;
convert->displayUrl = displayUrl;
@@ -1223,15 +1278,7 @@ namespace App {
convert->author = author;
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
convert->pendingTill = pendingTill;
MainWidget *m = App::main();
WebPageItems::const_iterator j = ::webPageItems.constFind(convert);
if (j != ::webPageItems.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
if (m) m->itemResized(k.key());
}
}
if (App::main()) App::main()->webPageUpdated(convert);
}
}
WebPagesData::const_iterator i = webPagesData.constFind(webPage);
@@ -1240,7 +1287,7 @@ namespace App {
if (convert) {
result = convert;
} else {
result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, photo, duration, author, pendingTill);
result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, photo, duration, author, (pendingTill >= -1) ? pendingTill : -1);
if (pendingTill > 0 && api()) {
api()->requestWebPageDelayed(result);
}
@@ -1249,7 +1296,7 @@ namespace App {
} else {
result = i.value();
if (result != convert) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill)) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) {
result->type = toWebPageType(type);
result->url = url;
result->displayUrl = displayUrl;
@@ -1261,15 +1308,7 @@ namespace App {
result->author = author;
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
result->pendingTill = pendingTill;
MainWidget *m = App::main();
WebPageItems::const_iterator j = ::webPageItems.constFind(result);
if (j != ::webPageItems.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
if (m) m->itemResized(k.key());
}
}
if (App::main()) App::main()->webPageUpdated(result);
}
}
}
@@ -1317,7 +1356,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));
}
@@ -1460,6 +1499,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;
}
@@ -1487,8 +1527,9 @@ namespace App {
if (api()) api()->clearWebPageRequests();
cSetRecentStickers(RecentStickerPack());
cSetStickersHash(QByteArray());
cSetStickers(AllStickers());
cSetEmojiStickers(EmojiStickersMap());
cSetStickerSets(StickerSets());
cSetLastStickersUpdate(0);
::videoItems.clear();
::audioItems.clear();
::documentItems.clear();
@@ -1539,19 +1580,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) {
@@ -1567,6 +1670,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();
@@ -1627,27 +1738,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();
}
@@ -1797,6 +1914,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());
@@ -1827,26 +1972,76 @@ 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 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)));
@@ -1962,7 +2157,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 = 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));
@@ -1975,27 +2180,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;
}

View File

@@ -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);
@@ -84,10 +109,10 @@ namespace App {
void feedWereDeleted(const QVector<MTPint> &msgsIds);
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links);
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
void feedMessageMedia(MsgId msgId, const MTPMessage &msg);
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);
@@ -118,8 +143,9 @@ 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);
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 = 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();
@@ -155,9 +181,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);
@@ -190,21 +217,35 @@ 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);
}
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) {

View File

@@ -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,7 +481,7 @@ 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;
@@ -654,9 +662,9 @@ void Application::checkMapVersion() {
psRegisterCustomScheme();
if (Local::oldMapVersion()) {
QString versionFeatures;
if (DevChannel && Local::oldMapVersion() < 8001) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Link previews for Twitter, YouTube, Instagram and certain other links\n\xe2\x80\x94 Two-step verification\n\xe2\x80\x94 View all your Telegram sessions, terminate specific sessions\n\xe2\x80\x94 Text is pasted from clipboard when clipboard has both text and image and image sending was cancelled").replace('@', qsl("@") + QChar(0x200D));
} else if (!DevChannel && Local::oldMapVersion() < 8002) {
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() < 8013) {
versionFeatures = lang(lng_new_version_text).trimmed();
}
if (!versionFeatures.isEmpty()) {
@@ -672,6 +680,8 @@ void Application::startApp() {
DEBUG_LOG(("Application Info: starting app.."));
QMimeDatabase().mimeTypeForName(qsl("text/plain")); // create mime database
window->createWinId();
window->init();

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

View File

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 215 KiB

View File

@@ -414,10 +414,6 @@ void VoiceMessagesFader::onTimer() {
if (m.state == VoiceMessagePausing || m.state == VoiceMessageFinishing) {
newGain = 1. - newGain;
}
if (newGain < 0) {
int a = 0, b;
b = a;
}
alSourcef(m.source, AL_GAIN, newGain);
}
} else if (playing && (state == AL_PLAYING || !m.loading)) {

View File

@@ -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())));
}
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -38,6 +38,7 @@ private:
void fillBlocks();
int32 _esize;
BottomButton _done;
int32 _blockHeight;

View File

@@ -30,7 +30,7 @@ _about(st::boxWidth - st::addContactPadding.left() - st::addContactPadding.right
_saveButton(this, lang(_turningOff ? lng_passcode_remove_button : lng_settings_save), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_oldPasscode(this, st::inpAddContact, lang(lng_passcode_enter_old)),
_newPasscode(this, st::inpAddContact, lang(lng_passcode_enter_new)),
_newPasscode(this, st::inpAddContact, lang(cHasPasscode() ? lng_passcode_enter_new : lng_passcode_enter_first)),
_reenterPasscode(this, st::inpAddContact, lang(lng_passcode_confirm_new)),
_passwordHint(this, st::inpAddContact, lang(lng_cloud_password_hint)),
_recoverEmail(this, st::inpAddContact, lang(lng_cloud_password_email)),
@@ -45,7 +45,7 @@ _about(st::boxWidth - st::addContactPadding.left() - st::addContactPadding.right
_saveButton(this, lang(_turningOff ? lng_passcode_remove_button : lng_settings_save), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_oldPasscode(this, st::inpAddContact, lang(lng_cloud_password_enter_old)),
_newPasscode(this, st::inpAddContact, lang(lng_cloud_password_enter_new)),
_newPasscode(this, st::inpAddContact, lang(curSalt.isEmpty() ? lng_cloud_password_enter_first : lng_cloud_password_enter_new)),
_reenterPasscode(this, st::inpAddContact, lang(lng_cloud_password_confirm_new)),
_passwordHint(this, st::inpAddContact, lang(lng_cloud_password_hint)),
_recoverEmail(this, st::inpAddContact, lang(lng_cloud_password_email)),
@@ -459,7 +459,7 @@ bool PasscodeBox::recoverStartFail(const RPCError &error) {
}
RecoverBox::RecoverBox(const QString &pattern) :
_submitRequest(0), _pattern(lng_signin_recover_hint(lt_recover_email, pattern)),
_submitRequest(0), _pattern(st::usernameFont->m.elidedText(lng_signin_recover_hint(lt_recover_email, pattern), Qt::ElideRight, st::boxWidth - st::addContactPadding.left() - st::addContactPadding.right())),
_saveButton(this, lang(lng_passcode_submit), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_recoverCode(this, st::inpAddContact, lang(lng_signin_code)) {
@@ -509,7 +509,7 @@ void RecoverBox::paintEvent(QPaintEvent *e) {
p.setFont(st::usernameFont->f);
int32 w = width() - st::addContactPadding.left() - st::addContactPadding.right();
p.drawText(QRect(st::addContactPadding.left(), _recoverCode.y() - st::usernameSkip - st::addContactPadding.top(), w, st::addContactPadding.top() + st::usernameSkip), st::usernameFont->m.elidedText(_pattern, Qt::ElideRight, w), style::al_center);
p.drawText(QRect(st::addContactPadding.left(), _recoverCode.y() - st::usernameSkip - st::addContactPadding.top(), w, st::addContactPadding.top() + st::usernameSkip), _pattern, style::al_center);
if (!_error.isEmpty()) {
p.setPen(st::setErrColor->p);

View File

@@ -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));

View File

@@ -36,8 +36,8 @@ 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.width + st::sessionTerminateSkip;
int32 w = width() - 2 * x, availw = width() - 2 * xact;
int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip;
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,11 +52,13 @@ 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::sessionInfoColor->p);
p.setPen(st::black->p);
p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth);
p.setPen(st::sessionInfoColor->p);
p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth);
p.translate(0, st::sessionHeight);
}
@@ -130,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) {
@@ -173,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() {
@@ -210,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);
@@ -221,9 +223,10 @@ void SessionsBox::paintEvent(QPaintEvent *e) {
p.drawTextRight(x, st::sessionPadding.top(), w, _current.active, _current.activeWidth);
p.setFont(st::sessionInfoFont->f);
p.setPen(st::sessionInfoColor->p);
p.setPen(st::black->p);
p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, _current.info, _current.infoWidth);
p.setPen(st::sessionInfoColor->p);
p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, _current.ip, _current.ipWidth);
p.translate(0, st::sessionHeight);
if (_list.isEmpty()) {
paintTitle(p, lang(lng_sessions_no_other), true);
@@ -245,7 +248,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
_shortPollRequest = 0;
int32 availCurrent = st::boxWidth - st::sessionPadding.left() - st::sessionTerminateSkip;
int32 availOther = availCurrent - st::sessionTerminate.width - st::sessionTerminateSkip;
int32 availOther = availCurrent - st::sessionTerminate.iconPos.x();// -st::sessionTerminate.width - st::sessionTerminateSkip;
_list.clear();
const QVector<MTPAuthorization> &v(result.c_account_authorizations().vauthorizations.c_vector().v);
@@ -259,29 +262,39 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
SessionData data;
data.hash = d.vhash.v;
QString appName, systemVer = qs(d.vsystem_version), deviceModel = qs(d.vdevice_model);
if (d.vapi_id.v == 17349) {
appName = qs(d.vapp_name);// (d.vapi_id.v == 2040) ? qsl("Telegram Desktop") : qsl("Telegram Desktop (GitHub)");
if (systemVer == QLatin1String("windows")) {
deviceModel = qsl("Windows");
} else if (systemVer == QLatin1String("os x")) {
deviceModel = qsl("Mac OS X");
} else if (systemVer == QLatin1String("linux")) {
deviceModel = qsl("Linux");
QString appName, appVer = qs(d.vapp_version), systemVer = qs(d.vsystem_version), deviceModel = qs(d.vdevice_model);
if (d.vapi_id.v == 2040 || d.vapi_id.v == 17349) {
appName = (d.vapi_id.v == 2040) ? qsl("Telegram Desktop") : qsl("Telegram Desktop (GitHub)");
// if (systemVer == QLatin1String("windows")) {
// deviceModel = qsl("Windows");
// } else if (systemVer == QLatin1String("os x")) {
// deviceModel = qsl("OS X");
// } else if (systemVer == QLatin1String("linux")) {
// deviceModel = qsl("Linux");
// }
if (appVer == QString::number(appVer.toInt())) {
int32 ver = appVer.toInt();
appVer = QString("%1.%2").arg(ver / 1000000).arg((ver % 1000000) / 1000) + ((ver % 1000) ? ('.' + QString::number(ver % 1000)) : QString());
} else {
appVer = QString();
}
} else {
appName = qs(d.vapp_name);// +qsl(" for ") + qs(d.vplatform);
if (appVer.indexOf('(') >= 0) appVer = appVer.mid(appVer.indexOf('('));
}
data.name = appName;
if (!appVer.isEmpty()) data.name += ' ' + appVer;
data.nameWidth = st::sessionNameFont->m.width(data.name);
QString country = qs(d.vcountry);
CountriesByISO2::const_iterator j = countries.constFind(country);
if (j != countries.cend()) country = QString::fromUtf8(j.value()->name);
QString country = qs(d.vcountry), platform = qs(d.vplatform);
//CountriesByISO2::const_iterator j = countries.constFind(country);
//if (j != countries.cend()) country = QString::fromUtf8(j.value()->name);
MTPint active = d.vdate_active.v ? d.vdate_active : d.vdate_created;
data.activeTime = active.v;
data.info = country + QLatin1String(" (") + qs(d.vip) + QLatin1String("), ") + deviceModel;
data.info = qs(d.vdevice_model) + QLatin1String(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(d.vsystem_version);
data.ip = qs(d.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
if (!data.hash || (d.vflags.v & 1)) {
data.active = QString();
data.activeWidth = 0;
@@ -294,6 +307,11 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
data.info = st::sessionInfoFont->m.elidedText(data.info, Qt::ElideRight, availCurrent);
data.infoWidth = st::sessionInfoFont->m.width(data.info);
}
data.ipWidth = st::sessionInfoFont->m.width(data.ip);
if (data.ipWidth > availCurrent) {
data.ip = st::sessionInfoFont->m.elidedText(data.ip, Qt::ElideRight, availCurrent);
data.ipWidth = st::sessionInfoFont->m.width(data.ip);
}
_current = data;
} else {
QDateTime now(QDateTime::currentDateTime()), lastTime(date(active));
@@ -317,6 +335,11 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
data.info = st::sessionInfoFont->m.elidedText(data.info, Qt::ElideRight, availOther);
data.infoWidth = st::sessionInfoFont->m.width(data.info);
}
data.ipWidth = st::sessionInfoFont->m.width(data.ip);
if (data.ipWidth > availOther) {
data.ip = st::sessionInfoFont->m.elidedText(data.ip, Qt::ElideRight, availOther);
data.ipWidth = st::sessionInfoFont->m.width(data.ip);
}
_list.push_back(data);
for (int32 i = _list.size(); i > 1;) {

View File

@@ -25,8 +25,8 @@ struct SessionData {
uint64 hash;
int32 activeTime;
int32 nameWidth, activeWidth, infoWidth;
QString name, active, info;
int32 nameWidth, activeWidth, infoWidth, ipWidth;
QString name, active, info, ip;
};
typedef QList<SessionData> SessionsList;

View File

@@ -0,0 +1,287 @@
/*
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());
StickerSets::iterator custom = sets.find(CustomStickerSetId);
sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName)).value().stickers = _pack;
if (custom != sets.cend()) {
for (int32 i = 0, l = _pack.size(); i < l; ++i) {
int32 index = custom->stickers.indexOf(_pack.at(i));
if (index >= 0) {
custom->stickers.removeAt(index);
}
}
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);
}

View 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;
};

View File

@@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 8001;
static const wchar_t *AppVersionStr = L"0.8.1";
static const int32 AppVersion = 8015;
static const wchar_t *AppVersionStr = L"0.8.15";
static const bool DevChannel = true;
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
@@ -101,9 +101,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,
@@ -218,18 +219,20 @@ static const char *ApiHash = "344583e45741c457fe1862106095a5eb";
inline const char *cApiDeviceModel() {
#ifdef Q_OS_WIN
return "x86 desktop";
#else
return "x64 desktop";
return "PC";
#elif defined Q_OS_MAC
return "Mac";
#elif defined Q_OS_LINUX
return "PC";
#endif
}
inline const char *cApiSystemVersion() {
#ifdef Q_OS_WIN
return "windows";
return "Windows";
#elif defined Q_OS_MAC
return "os x";
return "OS X";
#elif defined Q_OS_LINUX
return "linux";
return "Linux";
#endif
}
inline QString cApiAppVersion() {
@@ -253,6 +256,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
@@ -277,8 +282,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

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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(StickerSets::const_iterator it);
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;
//
//};

View File

@@ -38,7 +38,7 @@ 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);
}
}
queue.insert(msgId, File(media));

View File

@@ -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,113 @@ namespace anim {
}
}
bool AnimatedGif::animStep(float64 ms) {
int32 f = frame;
while (f < frames.size() && ms > delays[f]) {
++f;
if (f == frames.size() && frames.size() < framesCount) {
if (reader->read(&img)) {
int64 d = reader->nextImageDelay(), delay = delays[f - 1];
if (!d) d = 1;
delay += d;
frames.push_back(QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly));
delays.push_back(delay);
for (int32 i = 0; i < frames.size(); ++i) {
if (!frames[i].isNull()) {
frames[i] = QPixmap();
break;
}
}
} else {
framesCount = frames.size();
}
}
if (f == frames.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 (frames[f].isNull()) {
QString fname = reader->fileName();
delete reader;
reader = new QImageReader(fname);
}
}
if (frames[f].isNull() && reader->read(&img)) {
frames[f] = QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
}
}
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);
delays.reserve(framesCount);
int32 sizeLeft = MediaViewImageSizeLimit, delay = 0;
for (bool read = reader->read(&img); read; read = reader->read(&img)) {
sizeLeft -= w * h * 4;
frames.push_back(QPixmap::fromImage(img.size() == s ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly));
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();
delays.clear();
w = h = frame = framesCount = duration = 0;
anim::stop(this);
if (row && !onItemRemoved) {
row->initDimensions();
if (App::main()) App::main()->itemResized(row, true);
}
}

View File

@@ -185,6 +185,7 @@ namespace anim {
};
void start(Animated *obj);
void step(Animated *obj);
void stop(Animated *obj);
void startManager();
@@ -248,6 +249,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 +325,39 @@ 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);
}
signals:
void updated();
public:
HistoryItem *msg;
QImage img;
QImageReader *reader;
QVector<QPixmap> frames;
QVector<int64> delays;
int32 w, h, frame, framesCount, duration;
};

View File

@@ -20,11 +20,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "boxshadow.h"
BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntRetinaFactor()) {
if (!_size) return;
QImage cornersImage(_size * 2, _size * 2, QImage::Format_ARGB32_Premultiplied);
{
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 (
@@ -58,6 +61,8 @@ BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntR
}
void BoxShadow::paint(QPainter &p, const QRect &box, const QPoint &shift, int32 flags) {
if (!_size) return;
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);

View File

@@ -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;

View File

@@ -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,7 +142,7 @@ 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);
}
@@ -152,6 +153,9 @@ void ContextMenu::keyPressEvent(QKeyEvent *e) {
emit _buttons[_selected]->clicked();
return;
}
} else if (e->key() == Qt::Key_Escape) {
hideStart();
return;
}
if ((e->key() != Qt::Key_Up && e->key() != Qt::Key_Down) || _buttons.size() < 1) return;
@@ -242,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());

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,119 @@ 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) {
QString tmp(ch, e - ch);
QByteArray tmp2 = tmp.toUtf8();
const char *tmp3 = tmp2.constData();
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;
@@ -40,7 +149,7 @@ inline bool emojiEdge(const QChar *ch) {
inline QString replaceEmojis(const QString &text) {
QString result;
LinkRanges lnkRanges = textParseLinks(text);
LinkRanges lnkRanges = textParseLinks(text, TextParseLinks | TextParseMentions | TextParseHashtags);
int32 currentLink = 0, lnkCount = lnkRanges.size();
const QChar *emojiStart = text.unicode(), *emojiEnd = emojiStart, *e = text.cend();
bool canFindEmoji = true, consumePrevious = false;
@@ -48,13 +157,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 +174,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 +209,5 @@ inline QString replaceEmojis(const QString &text) {
return result;
}
int emojiPackCount(DBIEmojiTab tab);
EmojiPack emojiPack(DBIEmojiTab tab);

View File

@@ -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;

View File

@@ -100,6 +100,7 @@ public:
void setOpacity(float64 o);
void setText(const QString &text);
QString getText() const;
public slots:

View File

@@ -42,15 +42,13 @@ namespace {
FlatInputStyle _flatInputStyle;
}
FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent), _oldtext(v), _kev(0), _customUpDown(false), _phVisible(!v.length()),
FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent), _fullph(pholder), _oldtext(v), _kev(0), _customUpDown(false), _phVisible(!v.length()),
a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
a_borderColor(st.borderColor->c), a_bgColor(st.bgColor->c), _notingBene(0), _st(st) {
resize(_st.width, _st.height);
setFont(_st.font->f);
setAlignment(_st.align);
_ph = _st.font->m.elidedText(pholder, Qt::ElideRight, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1);
QPalette p(palette());
p.setColor(QPalette::Text, _st.textColor->c);
@@ -184,6 +182,15 @@ void FlatInput::focusOutEvent(QFocusEvent *e) {
emit blurred();
}
void FlatInput::resizeEvent(QResizeEvent *e) {
int32 availw = width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1;
if (_st.font->m.width(_fullph) > availw) {
_ph = _st.font->m.elidedText(_fullph, Qt::ElideRight, availw);
} else {
_ph = _fullph;
}
}
QSize FlatInput::sizeHint() const {
return geometry().size();
}

View File

@@ -34,6 +34,7 @@ public:
void focusInEvent(QFocusEvent *e);
void focusOutEvent(QFocusEvent *e);
void keyPressEvent(QKeyEvent *e);
void resizeEvent(QResizeEvent *e);
void notaBene();
@@ -69,7 +70,7 @@ protected:
private:
QString _ph, _oldtext;
QString _ph, _fullph, _oldtext;
QKeyEvent *_kev;
bool _customUpDown;

View File

@@ -68,10 +68,6 @@ void FlatTextarea::onTouchTimer() {
_touchRightButton = true;
}
void FlatTextarea::insertFromMimeData(const QMimeData *source) {
QTextEdit::insertFromMimeData(source);
}
bool FlatTextarea::viewportEvent(QEvent *e) {
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
QTouchEvent *ev = static_cast<QTouchEvent*>(e);
@@ -178,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;
}
@@ -292,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;
@@ -376,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);
}
}
}
@@ -420,11 +413,111 @@ bool FlatTextarea::isRedoAvailable() const {
return _redoAvailable;
}
void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp!
LinkRanges newLinks;
QString text(toPlainText());
if (text.isEmpty()) {
if (!_links.isEmpty()) {
_links.clear();
emit linksChanged();
}
return;
}
initLinkSets();
int32 len = text.size();
const QChar *start = text.unicode(), *end = start + text.size();
for (int32 offset = 0, matchOffset = offset; offset < len;) {
QRegularExpressionMatch m = reDomain().match(text, matchOffset);
if (!m.hasMatch()) break;
int32 domainOffset = m.capturedStart();
QString protocol = m.captured(1).toLower();
QString topDomain = m.captured(3).toLower();
bool isProtocolValid = protocol.isEmpty() || validProtocols().contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
bool isTopDomainValid = !protocol.isEmpty() || validTopDomains().contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
QString forMailName = text.mid(offset, domainOffset - offset - 1);
QRegularExpressionMatch mMailName = reMailName().match(forMailName);
if (mMailName.hasMatch()) {
offset = matchOffset = m.capturedEnd();
continue;
}
}
if (!isProtocolValid || !isTopDomainValid) {
offset = matchOffset = m.capturedEnd();
continue;
}
QStack<const QChar*> parenth;
const QChar *domainEnd = start + m.capturedEnd(), *p = domainEnd;
for (; p < end; ++p) {
QChar ch(*p);
if (chIsLinkEnd(ch)) break; // link finished
if (chIsAlmostLinkEnd(ch)) {
const QChar *endTest = p + 1;
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
++endTest;
}
if (endTest >= end || chIsLinkEnd(*endTest)) {
break; // link finished at p
}
p = endTest;
ch = *p;
}
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
parenth.push(p);
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
if (parenth.isEmpty()) break;
const QChar *q = parenth.pop(), open(*q);
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
p = q;
break;
}
}
}
if (p > domainEnd) { // check, that domain ended
if (domainEnd->unicode() != '/' && domainEnd->unicode() != '?') {
matchOffset = domainEnd - start;
continue;
}
}
newLinks.push_back(qMakePair(domainOffset - 1, p - start - domainOffset + 2));
offset = matchOffset = p - start;
}
if (newLinks != _links) {
_links = newLinks;
emit linksChanged();
}
}
QStringList FlatTextarea::linksList() const {
QStringList result;
if (!_links.isEmpty()) {
QString text(toPlainText());
for (LinkRanges::const_iterator i = _links.cbegin(), e = _links.cend(); i != e; ++i) {
result.push_back(text.mid(i->first + 1, i->second - 2));
}
}
return result;
}
void FlatTextarea::insertFromMimeData(const QMimeData *source) {
QTextEdit::insertFromMimeData(source);
emit spacedReturnedPasted();
}
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());
@@ -450,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;
}
@@ -512,6 +592,22 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
}
void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) {
if (!_links.isEmpty()) {
bool changed = false;
for (LinkRanges::iterator i = _links.begin(); i != _links.end();) {
if (i->first + i->second <= position) {
++i;
} else if (i->first >= position + charsRemoved) {
i->first += charsAdded - charsRemoved;
++i;
} else {
i = _links.erase(i);
changed = true;
}
}
if (changed) emit linksChanged();
}
if (_replacingEmojis || document()->availableRedoSteps() > 0) return;
const int takeBack = 3;
@@ -608,10 +704,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();
@@ -626,6 +728,13 @@ void FlatTextarea::keyPressEvent(QKeyEvent *e) {
if (enter && ctrl) {
e->setModifiers(e->modifiers() & ~Qt::ControlModifier);
}
bool spaceOrReturn = false;
QString t(e->text());
if (!t.isEmpty() && t.size() < 3) {
if (t.at(0) == '\n' || t.at(0) == '\r' || t.at(0).isSpace() || t.at(0) == QChar::LineSeparator) {
spaceOrReturn = true;
}
}
QTextEdit::keyPressEvent(e);
if (tc == textCursor()) {
bool check = false;
@@ -644,6 +753,7 @@ void FlatTextarea::keyPressEvent(QKeyEvent *e) {
}
}
}
if (spaceOrReturn) emit spacedReturnedPasted();
}
}

View File

@@ -27,9 +27,6 @@ class FlatTextarea : public QTextEdit, public Animated {
public:
FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &ph = QString(), const QString &val = QString());
QString val() const;
void insertFromMimeData(const QMimeData *source);
bool viewportEvent(QEvent *e);
void touchEvent(QTouchEvent *e);
@@ -59,6 +56,11 @@ public:
bool isUndoAvailable() const;
bool isRedoAvailable() const;
void parseLinks();
QStringList linksList() const;
void insertFromMimeData(const QMimeData *source);
public slots:
void onTouchTimer();
@@ -77,6 +79,8 @@ signals:
void submitted(bool ctrlShiftEnter);
void cancelled();
void tabbed();
void spacedReturnedPasted();
void linksChanged();
protected:
@@ -108,4 +112,8 @@ private:
typedef QPair<int, int> Insertion;
typedef QList<Insertion> Insertions;
Insertions _insertions;
typedef QPair<int, int> LinkRange;
typedef QList<LinkRange> LinkRanges;
LinkRanges _links;
};

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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,6 +591,7 @@ void ScrollArea::keyPressEvent(QKeyEvent *e) {
}
void ScrollArea::enterEvent(QEvent *e) {
if (_disabled) return;
if (_st.hiding) {
hor.hideTimeout(_st.hiding);
vert.hideTimeout(_st.hiding);

View File

@@ -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,10 @@ signals:
void scrollFinished();
void geometryChanged();
protected:
void scrollContentsBy(int dx, int dy);
private:
bool touchScroll(const QPoint &delta);
@@ -156,6 +161,8 @@ private:
void touchUpdateSpeed();
void touchDeaccelerate(int32 elapsed);
bool _disabled;
style::flatScroll _st;
ScrollBar hor, vert;
ScrollShadow topSh, bottomSh;

View File

@@ -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()));
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,19 @@ QString textAccentFold(const QString &text);
QString textSearchKey(const QString &text);
bool textSplit(QString &sendingText, QString &leftText, int32 limit);
enum {
TextParseMultiline = 0x001,
TextParseLinks = 0x002,
TextParseRichText = 0x004,
TextParseMentions = 0x008,
TextParseHashtags = 0x010,
TextTwitterMentions = 0x020,
TextTwitterHashtags = 0x040,
TextInstagramMentions = 0x080,
TextInstagramHashtags = 0x100,
};
struct LinkRange {
LinkRange() : from(0), len(0) {
}
@@ -32,10 +45,12 @@ struct LinkRange {
int32 len;
};
typedef QVector<LinkRange> LinkRanges;
LinkRanges textParseLinks(const QString &text, bool rich = false);
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 {
@@ -387,12 +402,6 @@ enum TextCommands {
TextCommandLangTag = 0x20,
};
enum {
TextParseMultiline = 0x01,
TextParseLinks = 0x02,
TextParseRichText = 0x04,
};
struct TextParseOptions {
int32 flags;
int32 maxw;
@@ -417,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);
@@ -425,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();
}
@@ -492,6 +506,11 @@ private:
};
void initLinkSets();
const QSet<int32> &validProtocols();
const QSet<int32> &validTopDomains();
const QRegularExpression &reDomain();
const QRegularExpression &reMailName();
const QRegularExpression &reHashtag();
// text style
@@ -520,4 +539,111 @@ 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);
}
inline bool chIsBad(QChar ch) {
return (ch == 0) || (ch >= 8232 && ch < 8239) || (ch >= 65024 && ch < 65040 && ch != 65039) || (ch >= 127 && ch < 160 && ch != 156);
}
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
QChar::Category c = ch.category();
return (c == QChar::Mark_NonSpacing);
}
inline int32 chMaxDiacAfterSymbol() {
return 2;
}
inline bool chIsNewline(QChar ch) {
return (ch == QChar::LineFeed || ch == 156);
}
inline bool chIsLinkEnd(QChar ch) {
return ch == TextCommand || chIsBad(ch) || chIsSpace(ch) || chIsNewline(ch) || ch.isLowSurrogate() || ch.isHighSurrogate();
}
inline bool chIsAlmostLinkEnd(QChar ch) {
switch (ch.unicode()) {
case '?':
case ',':
case '.':
case '"':
case ':':
case '!':
case '\'':
return true;
default:
break;
}
return false;
}
inline bool chIsWordSeparator(QChar ch) {
switch (ch.unicode()) {
case QChar::Space:
case QChar::LineFeed:
case '.':
case ',':
case '?':
case '!':
case '@':
case '#':
case '$':
case ':':
case ';':
case '-':
case '<':
case '>':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case '=':
case '/':
case '+':
case '%':
case '&':
case '^':
case '*':
case '\'':
case '"':
case '`':
case '~':
case '|':
return true;
default:
break;
}
return false;
}
inline bool chIsSentenceEnd(QChar ch) {
switch (ch.unicode()) {
case '.':
case '?':
case '!':
return true;
default:
break;
}
return false;
}
inline bool chIsSentencePartEnd(QChar ch) {
switch (ch.unicode()) {
case ',':
case ':':
case ';':
return true;
default:
break;
}
return false;
}
inline bool chIsParagraphSeparator(QChar ch) {
switch (ch.unicode()) {
case QChar::LineFeed:
return true;
default:
break;
}
return false;
}

View File

@@ -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);

View File

@@ -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:

File diff suppressed because it is too large Load Diff

View File

@@ -218,6 +218,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();
@@ -245,6 +248,7 @@ struct History : public QList<HistoryBlock*> {
QString draft;
MsgId draftToId;
MessageCursor draftCursor;
bool draftPreviewCancelled;
int32 lastWidth, lastScrollTop;
bool mute;
@@ -619,6 +623,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 {
@@ -654,6 +660,9 @@ public:
bool detached() const {
return !_block;
}
void attach(HistoryBlock *block) {
_block = block;
}
bool out() const {
return _flags & MTPDmessage_flag_out;
}
@@ -664,6 +673,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;
}
@@ -721,13 +736,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;
}
@@ -778,6 +805,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();
}
@@ -785,7 +815,7 @@ public:
w = qMin(width, _maxw);
return _height;
}
virtual TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
virtual void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
virtual void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const = 0;
virtual bool uploading() const {
return false;
@@ -801,6 +831,10 @@ public:
virtual void updateFrom(const MTPMessageMedia &media) {
}
virtual bool isImageLink() const {
return false;
}
virtual bool updateStickerEmoji() {
return false;
}
@@ -829,7 +863,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();
@@ -842,8 +876,9 @@ 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;
TextLinkPtr getLink(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;
PhotoData *photo() const {
@@ -869,6 +904,7 @@ public:
private:
int16 pixw, pixh;
PhotoData *data;
Text _caption;
TextLinkPtr openl;
};
@@ -878,17 +914,18 @@ QString formatSizeText(qint64 size);
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;
}
const QString inDialogsText() const;
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
TextLinkPtr getLink(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;
bool uploading() const {
return (data->status == FileUploading);
}
@@ -906,6 +943,8 @@ private:
VideoData *data;
TextLinkPtr _openl, _savel, _cancell;
Text _caption;
QString _size;
int32 _thumbw, _thumbx, _thumby;
@@ -926,7 +965,7 @@ public:
const QString inDialogsText() const;
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
TextLinkPtr getLink(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;
bool uploading() const {
return (data->status == FileUploading);
}
@@ -963,7 +1002,7 @@ public:
bool uploading() const {
return (data->status == FileUploading);
}
TextLinkPtr getLink(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;
DocumentData *document() {
@@ -1008,7 +1047,7 @@ public:
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
int32 countHeight(const HistoryItem *parent, int32 width = -1) const;
TextLinkPtr getLink(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;
DocumentData *document() {
@@ -1043,7 +1082,7 @@ public:
const QString inDialogsText() const;
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width) const;
HistoryMedia *clone() const;
void updateFrom(const MTPMessageMedia &media);
@@ -1063,6 +1102,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;
@@ -1070,7 +1112,7 @@ public:
const QString inDialogsText() const;
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
TextLinkPtr getLink(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;
void regItem(HistoryItem *item);
@@ -1150,7 +1192,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);
@@ -1163,11 +1205,16 @@ public:
const QString inDialogsText() const;
const QString inHistoryText() const;
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
TextLinkPtr getLink(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;
bool isImageLink() const {
return true;
}
private:
ImageLinkData *data;
Text _title, _description;
TextLinkPtr link;
};
@@ -1180,6 +1227,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 &currentText);
void initMediaFromText(QString &currentText);
void initMediaFromDocument(DocumentData *doc);
@@ -1198,7 +1246,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);
@@ -1209,7 +1260,9 @@ public:
QString notificationText() const;
void updateMedia(const MTPMessageMedia &media) {
if (_media) _media->updateFrom(media);
if (_media) {
_media->updateFrom(media);
}
}
void updateStickerEmoji();
@@ -1221,8 +1274,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;
@@ -1235,6 +1288,13 @@ public:
return from();
}
HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
return this;
}
const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize
return this;
}
~HistoryMessage();
protected:
@@ -1259,10 +1319,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 {
@@ -1273,6 +1336,13 @@ public:
}
QString selectedText(uint32 selection) const;
HistoryForwarded *toHistoryForwarded() {
return this;
}
const HistoryForwarded *toHistoryForwarded() const {
return this;
}
protected:
QDateTime fwdDate;
@@ -1307,6 +1377,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 {

File diff suppressed because it is too large Load Diff

View File

@@ -298,6 +298,7 @@ public:
void updateTyping(bool typing = true);
// void updateStickerPan();
void updateRecentStickers();
void stickersInstalled(uint64 setId);
void typingDone(const MTPBool &result, mtpRequestId req);
void destroyData();
@@ -363,6 +364,9 @@ public:
void setReplyReturns(PeerId peer, const QList<MsgId> &replyReturns);
void calcNextReplyReturn();
void updatePreview();
void previewCancel();
~HistoryWidget();
signals:
@@ -374,7 +378,13 @@ public slots:
void onCancel();
void onReplyToMessage();
void onReplyForwardCancel();
void onReplyForwardPreviewCancel();
void onStickerPackInfo();
void onPreviewParse();
void onPreviewCheck();
void onPreviewTimeout();
void peerUpdated(PeerData *data);
void onPeerLoaded(PeerData *data);
@@ -439,10 +449,22 @@ private:
HistoryItem *_replyTo;
Text _replyToName, _replyToText;
int32 _replyToNameVersion;
IconedButton _replyForwardCancel;
IconedButton _replyForwardPreviewCancel;
void updateReplyToName();
void drawFieldBackground(QPainter &p);
QString _previewLinks;
WebPageData *_previewData;
typedef QMap<QString, WebPageId> PreviewCache;
PreviewCache _previewCache;
mtpRequestId _previewRequest;
Text _previewTitle, _previewDescription;
SingleTimer _previewTimer;
bool _previewCancelled;
void gotPreview(QString links, const MTPMessageMedia &media, mtpRequestId req);
bool _replyForwardPressed;
HistoryItem *_replyReturn;
QList<MsgId> _replyReturns;
@@ -454,10 +476,9 @@ 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);
void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0, bool *previewCancelled = 0);
void setFieldText(const QString &text);
QStringList getMediasFromMime(const QMimeData *d);

View File

@@ -153,6 +153,7 @@ void IntroCode::activate() {
callTimer.start(1000);
error = "";
errorAlpha = anim::fvalue(0);
sentCode = QString();
show();
code.setDisabled(false);
code.setFocus();

View File

@@ -249,7 +249,7 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
}
void IntroPwdCheck::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern));
_emailPattern = st::introFont->m.elidedText(lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern)), Qt::ElideRight, textRect.width());
update();
}

View File

@@ -16,7 +16,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
"lng_language_name" = "Deutsch";
"lng_switch_to_this" = "Auf Englisch zurücksetzen";
"lng_switch_to_this" = "Auf Deutsch zurücksetzen";
"lng_menu_contacts" = "Kontakte";
"lng_menu_settings" = "Einstellungen";
@@ -98,6 +98,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_intro_next" = "WEITER";
"lng_intro_finish" = "REGISTRIEREN";
"lng_intro_submit" = "SENDEN";
"lng_phone_ph" = "Deine Telefonnummer";
"lng_phone_title" = "Dein Telefon";
@@ -124,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "Dieses Bild kannst du so nicht senden.\nMöchtest du es als Datei verschicken?";
"lng_signin_title" = "Dein Kennwort";
"lng_signin_desc" = "Bitte Kennwort eingeben.";
"lng_signin_recover_desc" = "Bitte Code aus der E-Mail eingeben.";
"lng_signin_password" = "Dein Kennwort";
"lng_signin_code" = "Code aus der E-Mail";
"lng_signin_recover" = "Kennwort vergessen?";
"lng_signin_hint" = "Hinweis: {password_hint}";
"lng_signin_recover_hint" = "Code wurde an {recover_email} gesendet";
"lng_signin_bad_password" = "Falsches Kennwort eingegeben.";
"lng_signin_wrong_code" = "Ungültiger Code, bitte erneut versuchen.";
"lng_signin_try_password" = "Du hast keinen Zugang zu deinen E-Mails?";
"lng_signin_password_removed" = "Dein Kennwort wurde entfernt.\nRichte ein neues in den Einstellungen ein.";
"lng_signin_no_email_forgot" = "Da du keine Email Adresse hinterlegt hast, \nkannst du nur noch hoffen, dass dir dein Kennwort wieder einfällt oder du musst tatsächlich dein Telegram Konto zurücksetzen.";
"lng_signin_cant_email_forgot" = "Da du keine E-Mail Adresse hinterlegt hast, kannst du nur noch hoffen, dass dir dein Kennwort wieder einfällt oder du musst tatsächlich dein Telegram Konto zurücksetzen.";
"lng_signin_reset_account" = "Konto zurücksetzen";
"lng_sigin_sure_reset" = "Warnung!\n\nDu verlierst du alle Chats und Nachrichten,\nebenso deine geteilten Bilder und Videos.\n\nWirklich Konto zurücksetzen?";
"lng_sigin_reset" = "Zurücksetzen";
"lng_signup_title" = "Information und Bild";
"lng_signup_desc" = "Bitte trage deinen Namen ein \nund lade ein Bild hoch.";
@@ -208,7 +227,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_download_path_header" = "Wähle Speicherort";
"lng_download_path_default_radio" = "Telegram-Ordner für «Downloads»";
"lng_download_path_temp_radio" = "Temporärer Ordner (bis du dich abmeldest)";
"lng_download_path_dir_radio" = "Nutzerdefinierter Ordner, wird nur manuell geleert.";
"lng_download_path_dir_radio" = "Eigener Ordner, wird nur manuell geleert";
"lng_download_path_choose" = "Wähle Speicherort";
"lng_sure_clear_downloads" = "Willst du alle Downloads vom Temp-Ordner löschen? Das passiert automatisch wenn du dich abmeldest oder Telegram deinstallierst.";
"lng_download_path_failed" = "Download konnte nicht gestartet werden. Das kann am eingestellten Speicherort liegen.\n\nDu kannst den Speicherort in den Einstellungen ändern.";
@@ -229,7 +248,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "Erweitert";
"lng_passcode_turn_on" = "Code-Sperre aktivieren";
"lng_passcode_remove_button" = "Entfernen";
"lng_passcode_turn_on" = "Pincode aktivieren";
"lng_passcode_change" = "Pincode ändern";
"lng_passcode_create" = "Pincode festlegen";
"lng_passcode_remove" = "Pincode entfernen";
@@ -240,16 +261,41 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_autolock_minutes" = "{count:_not_used_|# Minute|# Minuten}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# Stunde|# Stunden}";
"lng_passcode_enter_old" = "Aktueller Pincode";
"lng_passcode_enter_first" = "Pincode festlegen";
"lng_passcode_enter_new" = "Neuer Pincode";
"lng_passcode_confirm_new" = "Pincode wiederholen";
"lng_passcode_about" = "Bei aktivierter Code-Sperre, erscheint ein Schloss oben in der Menüleiste. Klicke darauf, um die App zu sperren.\n\nÜbrigens: Vergisst du den Pincode, musst du dich abmelden und wieder anmelden.";
"lng_passcode_differ" = "Pincode ist nicht identisch";
"lng_passcode_wrong" = "Falscher Pincode";
"lng_passcode_is_same" = "Pincode wurde nicht geändert";
"lng_passcode_enter" = "Telegram Pincode eingeben";
"lng_passcode_enter" = "Pincode eingeben";
"lng_passcode_submit" = "Absenden";
"lng_passcode_logout" = "Abmelden";
"lng_cloud_password_waiting" = "Bestätigungslink gesendet an {email}..";
"lng_cloud_password_change" = "Kennwort ändern";
"lng_cloud_password_create" = "Kennwort erstellen";
"lng_cloud_password_remove" = "Kennwort entfernen";
"lng_cloud_password_set" = "Zweistufige Bestätigung aktivieren";
"lng_cloud_password_edit" = "Kennwort ändern";
"lng_cloud_password_enter_old" = "Aktuelles Kennwort";
"lng_cloud_password_enter_first" = "Kennwort eingeben";
"lng_cloud_password_enter_new" = "Neues Kennwort eingeben";
"lng_cloud_password_confirm_new" = "Kennwort wiederholen";
"lng_cloud_password_hint" = "Kennwort-Hinweis";
"lng_cloud_password_bad" = "Kennwort und Hinweis müssen sich unterscheiden.";
"lng_cloud_password_email" = "E-Mail für die Wiederherstellung";
"lng_cloud_password_bad_email" = "Ungültige E-Mai, bitte erneut versuchen.";
"lng_cloud_password_about" = "Du kannst ein eigenes Kennwort festlegen, um dich an einem neuen Gerät anzumelden, zusätzlich zum SMS-Code.";
"lng_cloud_password_about_recover" = "Hinweis! Möchtest du wirklich keine E-Mail\nAdresse hinterlegen? \n\nWenn dir dein Kennwort nicht mehr einfällt, \nverlierst du Zugriff auf dein Telegram Konto.";
"lng_cloud_password_almost" = "Ein Bestätigungslink wurde an\ndeine E-Mail Adresse gesendet.\n\nZweistufige Bestätigung wird aktiviert,\nsobald du den Link öffnest.";
"lng_cloud_password_was_set" = "Zweistufige Bestätigung aktiviert.";
"lng_cloud_password_updated" = "Kennwort wurde geändert";
"lng_cloud_password_removed" = "Zweistufige Bestätigung deaktiviert.";
"lng_cloud_password_differ" = "Kennwörter stimmen nicht überein";
"lng_cloud_password_wrong" = "Falsches Kennwort";
"lng_cloud_password_is_same" = "Kennwort wurde nicht geändert";
"lng_connection_type" = "Verbindungsart:";
"lng_connection_auto_connecting" = "Standard (verbinden..)";
"lng_connection_auto" = "Standard ({type} verwendet)";
@@ -264,8 +310,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_connection_user_ph" = "Benutzername";
"lng_connection_password_ph" = "Kennwort";
"lng_connection_save" = "Speichern";
"lng_settings_reset" = "Alle anderen Sitzungen beenden";
"lng_settings_reset_sure" = "Sicher, dass du alle anderen Geräte abmelden möchtest?";
"lng_settings_show_sessions" = "Sitzungen";
"lng_settings_reset" = "Alle anderen Geräte abmelden";
"lng_settings_reset_sure" = "Sicher, dass du alle anderen Geräte\nabmelden möchtest?";
"lng_settings_reset_one_sure" = "Dieses Gerät wirklich abmelden?";
"lng_settings_reset_button" = "Beenden";
"lng_settings_reset_done" = "Alle anderen Sitzungen wurden erfolgreich beendet.";
"lng_settings_logout" = "Abmelden";
@@ -275,10 +323,18 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "Neustart";
"lng_settings_restart_later" = "Später";
"lng_sessions_header" = "Aktuelles Gerät";
"lng_sessions_other_header" = "Andere Geräte";
"lng_sessions_no_other" = "Keine anderen Geräte";
"lng_sessions_other_desc" = "Du kannst Telegram gleichzeitig vom\nHandy, Tablet oder auch Computer mit\nder gleichen Handynummer benutzen.\nAlles wird immer synchronisiert.";
"lng_sessions_terminate_all" = "Alle beenden";
"lng_preview_loading" = "Lade Linkvorschau...";
"lng_profile_chat_unaccessible" = "Gruppe nicht verfügbar";
"lng_topbar_info" = "Info";
"lng_profile_settings_section" = "Einstellungen";
"lng_profile_participants_section" = "Mitglieder";
"lng_profile_participants_section" = "Teilnehmer";
"lng_profile_info" = "Kontaktinfo";
"lng_profile_group_info" = "Gruppeninfo";
"lng_profile_add_contact" = "Kontakt hinzufügen";
@@ -320,20 +376,31 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_sure_delete_and_exit" = "Deinen Verlauf von «{group}» löschen und die Gruppe verlassen?\n\nDas kann man nicht rückgängig machen.";
"lng_sure_enable_debug" = "Möchtest du in den DEBUG Modus wechseln?\n\nAlle Ereignisse werden aufgezeichnet.";
"lng_message_empty" = "(empty)";
"lng_action_add_user" = "{from} hat {user} hinzugefügt";
"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";
@@ -353,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|# Stickers} 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";
@@ -404,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";
@@ -473,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}";
@@ -487,11 +580,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "Bild wurde im [c]Downloads[/c] Ordner gespeichert";
"lng_new_authorization" = "{name},\n\nwir haben eine Anmeldung von einem neuen Gerät am {day}, {date} um {time} festgestellt. \n\nGerät: {device}\nStandort: {location}\n\nWarst du das selbst? Wenn du das nicht selbst gewesen bist, melde alle anderen Sitzungen in den Telegram Einstellungen unverzüglich ab. \n\nDanke,\nDein Telegram Team";
"lng_new_authorization" = "{name},\nwir haben eine Anmeldung von einem neuen Gerät am {day}, {date} um {time} festgestellt.\n\nGerät: {device}\nStandort: {location}\n\nWarst du das selbst? Wenn du das nicht selbst gewesen bist, melde alle anderen Sitzungen in den Telegram Einstellungen unverzüglich ab.\n\nBeachte unsere zweistufige Bestätigung, welche du in den Telegram Einstellungen optional aktivieren kannst.\n\nDanke,\nDein Telegram Team";
"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_version7026" = "— Kommentar vor weitergeleiteten Nachrichten hinzufügen\n— Hashtags Vorschläge in neuen Nachrichten und Suchfeldern (vorherige Suche wird berücksichtigt)\n— Knopf hinzugefügt, um schnell zurück zur Antwort zu gelangen, nachdem man die Originalnachricht angeklickt hat\n— Gruppenteilnehmer über Benutzernamen hinzufügen";
"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";

View File

@@ -89,7 +89,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_chat_status_members_online" = "{count:_not_used_|# miembro|# miembros}, {count_online:_not_used_|# en línea|# en línea}";
"lng_server_error" = "Error interno del servidor.";
"lng_flood_error" = "Muchos intentos. Por favor, prueba más tarde.";
"lng_flood_error" = "Muchos intentos. Por favor, reinténtalo más tarde.";
"lng_deleted" = "Desconocido";
"lng_deleted_message" = "Mensaje eliminado";
@@ -98,6 +98,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_intro_next" = "SIGUIENTE";
"lng_intro_finish" = "REGISTRARSE";
"lng_intro_submit" = "ENVIAR";
"lng_phone_ph" = "Tu número de teléfono";
"lng_phone_title" = "Tu teléfono";
@@ -124,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "La imagen no se puede enviar de este modo.\n¿Quieres enviarla como archivo?";
"lng_signin_title" = "Contraseña";
"lng_signin_desc" = "Por favor, pon tu contraseña.";
"lng_signin_recover_desc" = "Por favor, pon el código desde el e-mail.";
"lng_signin_password" = "Tu contraseña";
"lng_signin_code" = "Código desde el e-mail";
"lng_signin_recover" = "¿Olvidaste la contraseña?";
"lng_signin_hint" = "Pista: {password_hint}";
"lng_signin_recover_hint" = "El código fue enviado a {recover_email}";
"lng_signin_bad_password" = "Pusiste la contraseña equivocada.";
"lng_signin_wrong_code" = "Pusiste un código inválido. Por favor, reinténtalo.";
"lng_signin_try_password" = "¿Tienes problemas para acceder a tu e-mail?";
"lng_signin_password_removed" = "Tu contraseña fue desactivada.\nPuedes configurar una nueva en Ajustes.";
"lng_signin_no_email_forgot" = "Como no estableciste un e-mail de recuperación \ncuando configuraste tu contraseña, las opciones restantes son recordar tu contraseña o restablecer tu cuenta.";
"lng_signin_cant_email_forgot" = "Si no puedes acceder a tu e-mail, las opciones restantes son recordar tu contraseña o restablecer tu cuenta.";
"lng_signin_reset_account" = "Restablecer tu cuenta";
"lng_sigin_sure_reset" = "¡Advertencia!\n\n¡Perderás todos tus chats y mensajes,\njunto con la multimedia y archivos compartidos!\n\n¿Quieres restablecer tu cuenta?";
"lng_sigin_reset" = "Restablecer";
"lng_signup_title" = "Información y foto";
"lng_signup_desc" = "Por favor, pon tu nombre \ny una foto.";
@@ -142,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";
@@ -229,10 +248,12 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "Avanzado";
"lng_passcode_remove_button" = "Quitar";
"lng_passcode_turn_on" = "Activar código de acceso";
"lng_passcode_change" = "Cambiar código de acceso";
"lng_passcode_create" = "Crear código de acceso";
"lng_passcode_remove" = "Desactivar código de acceso";
"lng_passcode_remove" = "Quitar código de acceso";
"lng_passcode_turn_off" = "Desactivar";
"lng_passcode_autolock" = "Bloqueo automático";
"lng_passcode_autolock_away" = "Bloqueo automático si estoy fuera:";
@@ -240,9 +261,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuto|# minutos}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# hora|# horas}";
"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_about" = "Cuando configuras un código, aparece un candado en la pantalla de chats. 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_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";
"lng_passcode_is_same" = "El código no fue cambiado";
@@ -250,6 +272,30 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_submit" = "Enviar";
"lng_passcode_logout" = "Cerrar sesión";
"lng_cloud_password_waiting" = "Enlace de confirmación enviado a {email}...";
"lng_cloud_password_change" = "Cambiar contraseña";
"lng_cloud_password_create" = "Crear contraseña";
"lng_cloud_password_remove" = "Quitar contraseña";
"lng_cloud_password_set" = "Activar verificación en dos pasos";
"lng_cloud_password_edit" = "Cambiar contraseña";
"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" = "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";
"lng_cloud_password_bad_email" = "E-mail incorrecto. Por favor, prueba otro.";
"lng_cloud_password_about" = "Esta contraseña será requerida cuando inicies sesión en un nuevo dispositivo, además del código de activación.";
"lng_cloud_password_about_recover" = "¡Advertencia! ¿No quieres añadir un e-mail \nde recuperación para la contraseña?\n\nSi olvidas tu contraseña, perderás\nel acceso a tu cuenta de Telegram.";
"lng_cloud_password_almost" = "Un enlace de confirmación fue enviado\nal e-mail que estableciste.\n\nLa verificación en dos pasos se activará\nen cuanto sigas ese enlace.";
"lng_cloud_password_was_set" = "Verificación en dos pasos activada.";
"lng_cloud_password_updated" = "Tu contraseña fue actualizada.";
"lng_cloud_password_removed" = "Verificación en dos pasos desactivada.";
"lng_cloud_password_differ" = "Las contraseñas no coinciden";
"lng_cloud_password_wrong" = "Contraseña equivocada";
"lng_cloud_password_is_same" = "Contraseña no cambiada";
"lng_connection_type" = "Tipo de conexión:";
"lng_connection_auto_connecting" = "Por defecto (conectando...)";
"lng_connection_auto" = "Por defecto ({type} en uso)";
@@ -261,11 +307,13 @@ 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";
"lng_settings_reset" = "Cerrar todas las otras sesiones";
"lng_settings_reset_sure" = "¿Quieres cerrar todas las otras sesiones?";
"lng_settings_reset_sure" = "¿Quieres cerrar todas \nlas otras sesiones?";
"lng_settings_reset_one_sure" = "¿Quieres cerrar esta sesión?";
"lng_settings_reset_button" = "Cerrar";
"lng_settings_reset_done" = "Se cerraron las otras sesiones";
"lng_settings_logout" = "Cerrar sesión";
@@ -275,6 +323,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "Reiniciar";
"lng_settings_restart_later" = "Después";
"lng_sessions_header" = "Sesión actual";
"lng_sessions_other_header" = "Sesiones activas";
"lng_sessions_no_other" = "Sin otras sesiones";
"lng_sessions_other_desc" = "Puedes iniciar sesión en Telegram desde\notro móvil, tablet o computador, usando el\nmismo número de teléfono. Todos tus datos\n se sincronizarán al instante.";
"lng_sessions_terminate_all" = "Cerrar todas";
"lng_preview_loading" = "Obteniendo enlace...";
"lng_profile_chat_unaccessible" = "El grupo es inaccesible";
"lng_topbar_info" = "Información";
"lng_profile_settings_section" = "Ajustes";
@@ -294,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";
@@ -320,20 +376,31 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_sure_delete_and_exit" = "¿Quieres eliminar todo el historial de mensajes y dejar el grupo «{group}»?\n\nEsta acción no se puede deshacer.";
"lng_sure_enable_debug" = "¿Quieres activar el modo DEBUG?\n\nTodos los eventos de conexión serán registrados.";
"lng_message_empty" = "(vacío)";
"lng_action_add_user" = "{from} añadió a {user}";
"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";
@@ -353,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";
@@ -390,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";
@@ -404,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";
@@ -473,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}";
@@ -487,11 +580,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "La imagen fue guardada en tu carpeta [c]Descargas[/c]";
"lng_new_authorization" = "{name},\nDetectamos un inicio de sesión en tu cuenta desde un nuevo dispositivo el {day}, {date} a las {time}\n\nDispositivo: {device}\nUbicación: {location}\n\nSi no eras tú, puedes ir a Ajustes — Cerrar todas las otras sesiones.\n\nGracias.\nEl equipo de Telegram";
"lng_new_authorization" = "{name}:\nDetectamos un inicio de sesión en tu cuenta desde un nuevo dispositivo el {day}, {date} a las {time}\n\nDispositivo: {device}\nUbicación: {location}\n\nSi no eras tú, puedes ir a Ajustes — Mostrar todas las sesiones y cerrar esa sesión.\n\nSi crees que alguien inició tu sesión sin tu consentimiento, puedes activar la verificación en dos pasos en Ajustes.\n\nGracias.\nEl equipo de Telegram";
"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_version7026" = "— Añade un comentario antes de los mensajes reenviados\n— Sugerencias de hashtags en un mensaje nuevo o en las barras de búsqueda (basado en las búsquedas recientes)\n— Botón para volver a una respuesta después de ver el mensaje original\n— Añade personas a grupos a través de sus apodos";
"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";

View File

@@ -98,6 +98,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_intro_next" = "AVANTI";
"lng_intro_finish" = "ISCRIVITI";
"lng_intro_submit" = "INVIA";
"lng_phone_ph" = "Tuo numero di telefono";
"lng_phone_title" = "Tuo telefono";
@@ -124,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "L'immagine non può essere inviata in\nquesto modo. Vuoi inviarla come file?";
"lng_signin_title" = "Verifica password";
"lng_signin_desc" = "Per favore inserisci la tua password.";
"lng_signin_recover_desc" = "Per favore inserisci il codice dall'e-mail.";
"lng_signin_password" = "La tua password";
"lng_signin_code" = "Codice dall'e-mail";
"lng_signin_recover" = "Password dimenticata?";
"lng_signin_hint" = "Suggerimento: {password_hint}";
"lng_signin_recover_hint" = "Il codice è stato inviato a {recover_email}";
"lng_signin_bad_password" = "Hai inserito una password errata.";
"lng_signin_wrong_code" = "Hai inserito un codice non valido. Per favore riprova.";
"lng_signin_try_password" = "Hai problemi ad accedere alla tua e-mail?";
"lng_signin_password_removed" = "La tua password è stata disattivata.\nNe puoi impostare una nuova dalle Impostazioni.";
"lng_signin_no_email_forgot" = "Siccome non hai fornito un'email di recupero quando hai inserito la password, non ti resta che ricordarti la password o ripristinare il tuo account.";
"lng_signin_cant_email_forgot" = "Se non puoi recuperare l'accesso all'e-mail, non ti resta che ricordarti la password o ripristinare il tuo account.";
"lng_signin_reset_account" = "Ripristina il tuo account";
"lng_sigin_sure_reset" = "Attenzione!\n\nPerderai tutte le chat e i messaggi,\ninsieme ai media e ai file condivisi!\n\nVuoi ripristinare il tuo account?";
"lng_sigin_reset" = "Ripristina";
"lng_signup_title" = "Informazioni e foto";
"lng_signup_desc" = "Inserisci il tuo nome e\ncarica una foto.";
@@ -221,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!";
@@ -229,6 +248,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "Avanzate";
"lng_passcode_remove_button" = "Rimuovi";
"lng_passcode_turn_on" = "Attiva codice";
"lng_passcode_change" = "Cambia codice";
"lng_passcode_create" = "Crea codice";
@@ -240,16 +261,41 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuto|# minuti}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# ora|# ore}";
"lng_passcode_enter_old" = "Inserisci il tuo codice";
"lng_passcode_enter_first" = "Inserisci un codice";
"lng_passcode_enter_new" = "Inserisci il nuovo codice";
"lng_passcode_confirm_new" = "Reinserisci il nuovo codice";
"lng_passcode_about" = "Quando imposti un codice, un'icona col lucchetto apparirà nel menu in alto. Premi su di essa per bloccare l'app. \n\nNota: se ti dimentichi il codice, dovrai rifare il login su Telegram Desktop.";
"lng_passcode_differ" = "I codici sono diversi";
"lng_passcode_wrong" = "Codice errato";
"lng_passcode_is_same" = "Il codice non è stato cambiato";
"lng_passcode_enter" = "Inserisci il codice di Telegram";
"lng_passcode_enter" = "Inserisci il tuo codice";
"lng_passcode_submit" = "Invia";
"lng_passcode_logout" = "Disconnetti";
"lng_cloud_password_waiting" = "Link di conferma inviato a {email}..";
"lng_cloud_password_change" = "Cambia password";
"lng_cloud_password_create" = "Crea password";
"lng_cloud_password_remove" = "Rimuovi password";
"lng_cloud_password_set" = "Attiva verifica in due passaggi";
"lng_cloud_password_edit" = "Cambia password";
"lng_cloud_password_enter_old" = "Inserisci la vecchia password";
"lng_cloud_password_enter_first" = "Inserisci una password";
"lng_cloud_password_enter_new" = "Inserisci una nuova password";
"lng_cloud_password_confirm_new" = "Reinserisci la nuova password";
"lng_cloud_password_hint" = "Inserisci un suggerimento password";
"lng_cloud_password_bad" = "Password e suggerimento devono essere diversi.";
"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_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.";
"lng_cloud_password_removed" = "La verifica in due passaggi è stata disattivata.";
"lng_cloud_password_differ" = "Le password non corrispondono";
"lng_cloud_password_wrong" = "Password errata";
"lng_cloud_password_is_same" = "La password non è stata cambiata";
"lng_connection_type" = "Tipo di connessione:";
"lng_connection_auto_connecting" = "Default (connessione..)";
"lng_connection_auto" = "Default ({type} in uso)";
@@ -264,8 +310,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_connection_user_ph" = "Username";
"lng_connection_password_ph" = "Password";
"lng_connection_save" = "Salva";
"lng_settings_reset" = "Termina le altre sessioni";
"lng_settings_reset_sure" = "Sicuro di voler terminare tutte le altre sessioni?";
"lng_settings_show_sessions" = "Mostra tutte le sessioni";
"lng_settings_reset" = "Termina tutte le altre sessioni";
"lng_settings_reset_sure" = "Sicuro di voler terminare\ntutte le altre sessioni?";
"lng_settings_reset_one_sure" = "Vuoi terminare questa sessione?";
"lng_settings_reset_button" = "Chiudi";
"lng_settings_reset_done" = "Altre sessioni terminate";
"lng_settings_logout" = "Disconnetti";
@@ -275,6 +323,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "Riavvia";
"lng_settings_restart_later" = "Più tardi";
"lng_sessions_header" = "Sessione attuale";
"lng_sessions_other_header" = "Sessioni attive";
"lng_sessions_no_other" = "Nessun'altra sessione";
"lng_sessions_other_desc" = "Ti puoi connettere a Telegram da altri\ndispositivi mobili, tablet e desktop usando\nlo stesso numero. Tutti i tuoi dati saranno\nsincronizzati istantaneamente.";
"lng_sessions_terminate_all" = "Termina tutte";
"lng_preview_loading" = "Recupero le info del link..";
"lng_profile_chat_unaccessible" = "Gruppo non accessibile";
"lng_topbar_info" = "Info";
"lng_profile_settings_section" = "Impostazioni";
@@ -291,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";
@@ -302,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";
@@ -320,20 +376,31 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_sure_delete_and_exit" = "Sicuro di voler eliminare tutta la cronologia dei messaggi e abbandonare «{group}»?\n\nQuesta azione non può essere annullata.";
"lng_sure_enable_debug" = "Vuoi attivare la modalità DEBUG?\n\nTutti gli eventi di rete verranno registrati.";
"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";
@@ -345,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";
@@ -404,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";
@@ -473,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}";
@@ -487,11 +580,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "L'immagine è stata salvata nella tua cartella [c]Download[/c]";
"lng_new_authorization" = "{name},\nAbbiamo rilevato un accesso al tuo account da un nuovo dispositivo {day}, {date} alle {time}\n\nDispositivo: {device}\nPosizione: {location}\n\nSe non sei tu, puoi andare su Impostazioni Termina le altre sessioni. \n\nGrazie, \nIl Team di Telegram";
"lng_new_authorization" = "{name},\nAbbiamo rilevato un accesso al tuo account da un nuovo dispositivo il {day}, {date} alle {time}\n\nDispositivo: {device}\nPosizione: {location}\n\nSe non sei tu, puoi andare su Impostazioni Mostra tutte le sessioni e terminare quella sessione.\n\nSe credi che qualcuno si sia collegato al tuo account contro il tuo volere, puoi attivare la verifica in due passaggi nelle Impostazioni. \n\nGrazie, \nIl Team di Telegram";
"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_version7026" = "— Aggiungi un commento prima di inoltrare messaggi\n— Suggerimenti per gli hashtag in un nuovo messaggio e nella ricerca (basato sulle ricerche recenti)\n— Pulsante per tornare alla risposta dopo aver visualizzato il messaggio originale.\n— Aggiungi persone ai gruppi tramite username";
"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";

View File

@@ -59,45 +59,46 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_weekday6_full" = "토요일";
"lng_weekday7_full" = "일요일";
"lng_month_day" = "{month} {day} 일";
"lng_month_day" = "{month} {day}일";
"lng_cancel" = "취소";
"lng_continue" = "계속";
"lng_close" = "닫기";
"lng_connecting" = "연결중입니다..";
"lng_reconnecting" = " {count:현재| # 초 후에| # 초 후에} 연결 합니다..";
"lng_reconnecting" = " {count:지금| #초 후에| #초 후에} 다시 연결합니다..";
"lng_reconnecting_try_now" = "다시 시도";
"lng_status_service_notifications" = "서비스 공지사항";
"lng_status_service_notifications" = "서비스 알림";
"lng_status_offline" = "마지막으로 접속한 지 오래됨";
"lng_status_recently" = "최근에 접속";
"lng_status_last_week" = "일주일 이내 마지막으로 접속";
"lng_status_last_month" = "한 달 이내 마지막으로 접속";
"lng_status_invisible" = "숨김";
"lng_status_lastseen_now" = "방금 접속";
"lng_status_lastseen_minutes" = "접속한지 {count:_not_used_|# 분|# 분} 전";
"lng_status_lastseen_hours" = "접속한지 {count:_not_used_|# 시간|# 시간} 전";
"lng_status_lastseen_minutes" = "{count:_not_used_|#분|#분}전 접속";
"lng_status_lastseen_hours" = "{count:_not_used_|#시간|#시간}전 접속";
"lng_status_lastseen_today" = "오늘 {time}에 마지막으로 접속";
"lng_status_lastseen_yesterday" = "어제 {time}에 마지막으로 접속";
"lng_status_lastseen_date" = "{date}에 마지막으로 접속";
"lng_status_lastseen_date_time" = "{date} 일 {time} 에 마지막으로 접속";
"lng_status_lastseen_date_time" = "{date}일 {time}에 마지막으로 접속";
"lng_status_online" = "온라인";
"lng_status_connecting" = "연결중..";
"lng_chat_status_unaccessible" = "그룹 접근 불가";
"lng_chat_status_members" = "{count:없음|# 명|# 명}";
"lng_chat_status_members_online" = "{count:_not_used_|# 명|# 명}, {count_online:_not_used_|# 온라인|# 온라인}";
"lng_chat_status_members" = "{count:맴버 없음|#명|#명}";
"lng_chat_status_members_online" = "{count:_not_used_|#명|#명} {count_online:_not_used_|#명 접속중|#명 접속중}";
"lng_server_error" = "내부 서버 오류";
"lng_flood_error" = "시도가 너무 많습니다. 나중에 다시 시도해주세요.";
"lng_deleted" = "알 수 없음";
"lng_deleted_message" = "삭제된 메시지";
"lng_intro" = "[a href=\"https://telegram.org/\"]텔레그램[/a] PC 공식버전에 오신 것을 환영합니다.\n[b]안전[/b]하고 [b]신속[/b]하게 사용하세요.";
"lng_intro" = "[a href=\"https://telegram.org/\"]텔레그램[/a] PC 공식버전에 오신 것을 환영합니다.\n[b]안전[/b]하고 [b]신속[/b]합니다.";
"lng_start_msgs" = "시작하기";
"lng_intro_next" = "다음";
"lng_intro_finish" = "회원가입";
"lng_intro_submit" = "제출";
"lng_phone_ph" = "휴대폰 번호";
"lng_phone_title" = "전화번호";
@@ -112,7 +113,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_code_ph" = "코드번호";
"lng_code_desc" = "인증코드 메세지를 휴대폰으로 전송하였습니다.\n인증코드를 아래에 입력하여 주세요.";
"lng_code_call" = "텔레그램이 {minutes}:{seconds} 후에는 전화를 겁니다.";
"lng_code_call" = "텔레그램이 {minutes}:{seconds}후에는 전화를 겁니다.";
"lng_code_calling" = "텔레그램으로부터 전화 요청을 하고 있습니다..";
"lng_code_called" = "텔레그램이 회원님의 전화번호로 전화를 걸었습니다.";
@@ -124,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "이미지를 전송할 수 없습니다.\n파일로 전송을 하겠습니까?";
"lng_signin_title" = "클라우드 비밀번호 확인";
"lng_signin_desc" = "클라우드 비밀번호 입력";
"lng_signin_recover_desc" = "이메일에 기입된 코드를 입력해주세요";
"lng_signin_password" = "클라우드 비밀번호";
"lng_signin_code" = "이메일 코드";
"lng_signin_recover" = "비밀번호를 잊어버리셨습니까?";
"lng_signin_hint" = "힌트: {password_hint}";
"lng_signin_recover_hint" = "{recover_email} 로 코드를 전송하였습니다.";
"lng_signin_bad_password" = "잘못된 비밀번호를 입력하였습니다.";
"lng_signin_wrong_code" = "틀린 인증코드입니다. 다시 시도해주세요.";
"lng_signin_try_password" = "이메일 접근이 안되시나요?";
"lng_signin_password_removed" = "클라우드 비밀번호가 삭제되었습니다.\n설정에서 새로운 비밀번호 설정이 가능합니다.";
"lng_signin_no_email_forgot" = "비밀번호 복구 이메일을 설정하지 않았기때문에, \n비밀번호를 기억해내시거나 계정 초기화를 진행해주셔야합니다.";
"lng_signin_cant_email_forgot" = "이메일 접근을 하실 수 없을 경우, 비밀번호를 기억해내시거나 계정 초기화를 진행해주셔야 합니다.";
"lng_signin_reset_account" = "계정 초기화";
"lng_sigin_sure_reset" = "경고!\n\n계정 초기화 진행시 모든 대화,\n메시지 및 공유받은 미디어와 파일이 삭제가 됩니다.\n\n계정 초기화를 진행하시겠습니까?";
"lng_sigin_reset" = "초기화";
"lng_signup_title" = "개인정보 및 사진";
"lng_signup_desc" = "이름을 입력해주시고,\n사진을 업로드해주세요.";
@@ -134,8 +153,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_dlg_new_group_name" = "그룹이름";
"lng_dlg_create_group" = "만들기";
"lng_no_contacts" = "연락처가 없습니다.";
"lng_contacts_loading" = "로딩중입니다..";
"lng_contacts_not_found" = "검색결과가 없습니다.";
"lng_contacts_loading" = "로드중..";
"lng_contacts_not_found" = "연락처를 찾을 수 없음";
"lng_settings_save" = "저장";
"lng_settings_upload" = "프로필 이미지 선택";
@@ -146,37 +165,37 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_username_about" = "텔레그램 아이디를 설정할 수 있습니다. \n아이디를 설정하면 회원님의 전화번호를 몰라도 아이디로 회원님을 찾아 대화를 나눌 수 있습니다.\n아이디는 영문, 밑줄, 숫자로 a-z, _, 0-9, \n다섯 글자 이상으로 설정해 주세요.";
"lng_username_invalid" = "올바르지 않은 아이디입니다.";
"lng_username_occupied" = "사용중인 아이디입니다.";
"lng_username_too_short" = "아이디가 너무 짧습니다. (5글자 이상)";
"lng_username_too_short" = "아이디가 너무 짧습니다.";
"lng_username_bad_symbols" = "올바르지 않은 아이디입니다.";
"lng_username_available" = "사용가능한 아이디입니다.";
"lng_username_available" = "사용 가능한 아이디입니다.";
"lng_username_not_found" = "아이디 @{user}를 찾을 수 없습니다.";
"lng_settings_section_contact_info" = "연락처 정보";
"lng_settings_phone_number" = "전화번호";
"lng_settings_username" = "아이디";
"lng_settings_username" = "아이디:";
"lng_settings_choose_username" = "아이디 설정하기";
"lng_settings_section_notify" = "알림서비스";
"lng_settings_desktop_notify" = "데스크탑 알림서비스";
"lng_settings_section_notify" = "알림";
"lng_settings_desktop_notify" = "데스크탑 알림";
"lng_settings_show_name" = "발신인 표시";
"lng_settings_show_preview" = "메시지 미리보기";
"lng_settings_sound_notify" = "알림음";
"lng_settings_sound_notify" = "소리 재생";
"lng_notification_preview" = "새로운 메시지가 있습니다.";
"lng_settings_section_general" = "일반";
"lng_settings_change_lang" = "언어 변경";
"lng_languages" = "언어";
"lng_sure_save_language" = "언어 변경을 적용하기 위하여\n텔레그램을 재시작합니다.";
"lng_sure_save_language" = "언어 변경하기 위\n텔레그램을 재시작합니다.";
"lng_settings_auto_update" = "자동 업데이트";
"lng_settings_current_version" = "버전 {version}";
"lng_settings_current_version" = " {version}";
"lng_settings_check_now" = "업데이트 확인";
"lng_settings_update_checking" = "업데이트 확인 중..";
"lng_settings_latest_installed" = "최신 버전이 설치 되어 있습니다.";
"lng_settings_latest_installed" = "최신 버전이 설치되어 있습니다.";
"lng_settings_downloading" = "업데이트를 다운로드중입니다.. {ready} / {total} MB..";
"lng_settings_update_ready" = "새로운 버전을 설치 할 수 있습니다.";
"lng_settings_update_now" = "재시작 합니다.";
"lng_settings_update_fail" = "업데이트 확인 실패";
"lng_settings_update_fail" = "업데이트 확인 실패 :(";
"lng_settings_workmode_tray" = "트레이 아이콘 표시";
"lng_settings_workmode_window" = "테스크바 아이콘 표시";
"lng_settings_auto_start" = "시스템 시작시 자동실행";
@@ -187,7 +206,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_chat" = "채팅 설정";
"lng_settings_replace_emojis" = "이모티콘 자동변환";
"lng_settings_view_emojis" = "리스트 보기";
"lng_settings_view_emojis" = "목록 보기";
"lng_settings_emoji_list" = "지원되는 이모티콘 리스트";
"lng_settings_send_enter" = "엔터키로 메시지 전송";
"lng_settings_send_ctrlenter" = " Ctrl+Enter로 메시지 전송";
@@ -219,9 +238,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_download_path_clear_failed" = "초기화 실패 :(";
"lng_settings_section_cache" = "로컬 저장소";
"lng_settings_no_data_cached" = "캐시 데이터를 못 찾았습니다!";
"lng_settings_images_cached" = "{count:_not_used_|이미지 # 개|이미지 # 개}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|음성 메시지 # 개|음성 메시지 # 개}, {size}";
"lng_settings_no_data_cached" = "캐시 데이터가 없습니다!";
"lng_settings_images_cached" = "{count:_not_used_|이미지 #개|이미지 #개}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|음성 메시지 #개|음성 메시지 #개}, {size}";
"lng_local_storage_clear" = "전체 정리";
"lng_local_storage_clearing" = "초기화 중..";
"lng_local_storage_cleared" = "초기화 완료!";
@@ -229,43 +248,72 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "고급";
"lng_passcode_turn_on" = "잠금번호 사용";
"lng_passcode_change" = "잠금번호 변경";
"lng_passcode_create" = "잠금번호 생성";
"lng_passcode_remove" = "잠금번호 삭제";
"lng_passcode_turn_off" = "비활성화";
"lng_passcode_autolock" = "자동잠금";
"lng_passcode_remove_button" = "삭제";
"lng_passcode_turn_on" = "잠금코드 사용";
"lng_passcode_change" = "잠금코드 변경";
"lng_passcode_create" = "잠금코드 생성";
"lng_passcode_remove" = "잠금코드 삭제";
"lng_passcode_turn_off" = "끄기";
"lng_passcode_autolock" = "자동-잠금";
"lng_passcode_autolock_away" = "미사용시 자동잠금 설정";
"lng_passcode_autolock_inactive" = "메신져 미사용시 자동잠금 설정";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# 분|# 분} ";
"lng_passcode_autolock_hours" = "{count:_not_used_|# 시간|# 시간} ";
"lng_passcode_enter_old" = "기존 잠금번호 입력";
"lng_passcode_enter_new" = "신규 잠금번호 입력";
"lng_passcode_confirm_new" = "신규 잠금번호 입력";
"lng_passcode_autolock_minutes" = "{count:_not_used_|#분|#분} ";
"lng_passcode_autolock_hours" = "{count:_not_used_|#시간|#시간} ";
"lng_passcode_enter_old" = "기존 비밀번호 입력";
"lng_passcode_enter_first" = "잠금코드 입력";
"lng_passcode_enter_new" = "신규 비밀번호 입력";
"lng_passcode_confirm_new" = "신규 비밀번호 재입력";
"lng_passcode_about" = "잠금번호를 설정하시면 구석에 잠금 아이콘이 표시됩니다. 클릭을 하셔서 잠궈주세요.\n\n참고 : 잠금번호를 잊어버리셨을 경우 텔레그램 데스크탑을 재로그인을 해주셔야합니다.";
"lng_passcode_differ" = "잠금번호가 다릅니다.";
"lng_passcode_wrong" = "잘못된 잠금번호입니다.";
"lng_passcode_is_same" = "잠금번호가 변경되지 않았습니다.";
"lng_passcode_enter" = "잠금번호를 입력해주세요.";
"lng_passcode_differ" = "비밀번호가 다릅니다.";
"lng_passcode_wrong" = "잘못된 비밀번호입니다.";
"lng_passcode_is_same" = "비밀번호가 변경되지 않았습니다.";
"lng_passcode_enter" = "잠금코드 입력";
"lng_passcode_submit" = "입력";
"lng_passcode_logout" = "로그아웃";
"lng_cloud_password_waiting" = "{email}로 확인 이메일을 전송하였습니다..";
"lng_cloud_password_change" = "클라우드 비밀번호 변경";
"lng_cloud_password_create" = "클라우드 비밀번호 생성";
"lng_cloud_password_remove" = "클라우드 비밀번호 삭제";
"lng_cloud_password_set" = "2단계 인증 활성화";
"lng_cloud_password_edit" = "클라우드 비밀번호 변경";
"lng_cloud_password_enter_old" = "기존 비밀번호 입력";
"lng_cloud_password_enter_first" = "비밀번호 입력";
"lng_cloud_password_enter_new" = "새로운 비밀번호 입력";
"lng_cloud_password_confirm_new" = "새로운 비밀번호 재입력";
"lng_cloud_password_hint" = "비밀번호 힌트 입력";
"lng_cloud_password_bad" = "비밀번호와 힌트는 동일할 수 없습니다.";
"lng_cloud_password_email" = "복구 이메일 입력";
"lng_cloud_password_bad_email" = "올바르지 않은 이메일입니다. 다시 시도해주세요.";
"lng_cloud_password_about" = "핀코드와 별도로 새로운 기기에서 로그인하실때 사용되는 비밀번호입니다.";
"lng_cloud_password_about_recover" = "경고! 비밀번호 복구 이메일을 설정 안하시겠습니까?\n\n비밀번호 분실시 텔레그램 접근 권한이 유실됩니다.";
"lng_cloud_password_almost" = "제공하신 이메일로 확인링크를 전송하였습니다.\n\n링크를 누르시면 2단계 인증이 활성화 됩니다.";
"lng_cloud_password_was_set" = "2단계 인증이 활성화되었습니다.";
"lng_cloud_password_updated" = "클라우드 비밀번호가 변경되었습니다.";
"lng_cloud_password_removed" = "2단계 인증이 비활성화 되었습니다.";
"lng_cloud_password_differ" = "비밀번호가 일치하지 않습니다.";
"lng_cloud_password_wrong" = "올바르지 않은 클라우드 비밀번호입니다.";
"lng_cloud_password_is_same" = "비밀번호가 변경되지 않았습니다.";
"lng_connection_type" = "연결 유형:";
"lng_connection_auto_connecting" = "기본값 (연결중..)";
"lng_connection_auto" = "기본값 ({type} 사용)";
"lng_connection_http_proxy" = "HTTP 프록시";
"lng_connection_tcp_proxy" = "TCP 프록시";
"lng_connection_header" = "연결 유형";
"lng_connection_auto_rb" = "자동 (TCP 혹은 HTTP 사용)";
"lng_connection_http_proxy_rb" = "HTTP (커스텀 http프록시)";
"lng_connection_auto_rb" = "자동 (사용 가능하다면 TCP 아니면 HTTP 사용)";
"lng_connection_http_proxy_rb" = "HTTP (커스텀 HTTP 프록시)";
"lng_connection_tcp_proxy_rb" = "TCP (커스텀 socks5-proxy)";
"lng_connection_host_ph" = "호스트네임";
"lng_connection_port_ph" = "포트번호";
"lng_connection_port_ph" = "포트";
"lng_connection_user_ph" = "아이디";
"lng_connection_password_ph" = "비밀번호";
"lng_connection_save" = "저장";
"lng_settings_show_sessions" = "모든 세션 보기";
"lng_settings_reset" = "다른 모든 세션 강제 종료";
"lng_settings_reset_sure" = "현재 기기를 제외하고 다른 기기에 로그인된 세션을 모두 종료시킬까요?";
"lng_settings_reset_one_sure" = "해당 세션을 강제 종료 하시겠습니까?";
"lng_settings_reset_button" = "종료";
"lng_settings_reset_done" = "다른 모든 세션이 강제 종료 되었습니다.";
"lng_settings_logout" = "로그아웃";
@@ -275,10 +323,18 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "재시작";
"lng_settings_restart_later" = "나중에";
"lng_sessions_header" = "현재 세션";
"lng_sessions_other_header" = "활성화된 세션";
"lng_sessions_no_other" = "다른 세션이 없습니다.";
"lng_sessions_other_desc" = "동일한 휴대번호로 \n다른 휴대기기, 태블릿과 데스크탑에서 \n텔레그램 로그인이 가능합니다.\n 모든 데이터는 즉시 동기화 됩니다.";
"lng_sessions_terminate_all" = "모두 강제종료";
"lng_preview_loading" = "링크 정보를 가져오는 중..";
"lng_profile_chat_unaccessible" = "그룹에 접근할 수 없습니다.";
"lng_topbar_info" = "정보";
"lng_profile_settings_section" = "환경설정";
"lng_profile_participants_section" = "대화상대";
"lng_profile_participants_section" = "사용자";
"lng_profile_info" = "연락처 정보";
"lng_profile_group_info" = "그룹 정보";
"lng_profile_add_contact" = "연락처 추가";
@@ -296,13 +352,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_profile_loading" = "로딩중..";
"lng_profile_shared_media" = "공유된 미디어";
"lng_profile_no_media" = "대화에 미디어가 존재하지 않습니다.";
"lng_profile_photos" = "{count:_not_used_|# 사진|# 사진} »";
"lng_profile_photos" = "{count:_not_used_|#개의 사진|#개의 사진} »";
"lng_profile_photos_header" = "사진 내역";
"lng_profile_videos" = "{count:_not_used_|# 비디오 파일|# 비디오 파일} »";
"lng_profile_videos" = "{count:_not_used_|#개의 비디오 파일|#개의 비디오 파일} »";
"lng_profile_videos_header" = "비디오 파일 내역";
"lng_profile_files" = "{count:_not_used_|# 파일|# 파일} »";
"lng_profile_files" = "{count:_not_used_|#개의 파일|#개의 파일} »";
"lng_profile_files_header" = "파일 내역";
"lng_profile_audios" = "{count:_not_used_|# 음성 메시지|# 음성 메시지} »";
"lng_profile_audios" = "{count:_not_used_|#개의 음성 메시지|#개의 음성 메시지} »";
"lng_profile_audios_header" = "음성 메시지 내역";
"lng_profile_show_all_types" = "모든 유형 보기";
"lng_profile_copy_phone" = "전화번호 복사";
@@ -320,20 +376,31 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_sure_delete_and_exit" = "그룹 «{group}» 방에서 모든 메시지를 \n삭제하시고 퇴장하시겠습니까?\n\n삭제 하실 경우 취소가 불가능합니다.";
"lng_sure_enable_debug" = "DEBUG 모드를 화성화 시키시겠습니까?\n\n모든 네트워크 내역이 기록됩니다.";
"lng_message_empty" = "(없음)";
"lng_action_add_user" = "{from} 님께서 {user} 님을 초대하셨습니다.";
"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" = "다음 유저에게 답장 :";
@@ -353,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" = "연락처";
@@ -371,8 +458,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_typing" = "입력중";
"lng_user_typing" = "{user}님이 입력중입니다.";
"lng_users_typing" = "{user}님과 {second_user}님이 입력중입니다.";
"lng_many_typing" = "{count:_not_used_|# 명이|# 명이} 입력중입니다";
"lng_unread_bar" = "{count:_not_used_|# 읽지 않은 메시지|# 읽지 않은 메시지}";
"lng_many_typing" = "{count:_not_used_|#명이|#명이} 입력중입니다";
"lng_unread_bar" = "{count:_not_used_|#개의 읽지 않은 메시지|#개의 읽지 않은 메시지}";
"lng_maps_point" = "위치";
"lng_save_photo" = "사진 저장";
@@ -404,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" = "파일 전달";
@@ -473,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}";
@@ -487,11 +580,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "[c]다운로드[/c] 폴더로 이미지가 저장되었습니다.";
"lng_new_authorization" = "{name}님,\n{day}, {date} {time}에 새 기기에서 회원님의 계정 로그인하였습니다.\n\n기기: {device}\n위치: {location}\n\n본인의 접속이 아니라면 '설정'으로 가서 '다른 모든 세션 종료' 실행하세요.\n\n감사합니다.\n텔레그램 팀 드림";
"lng_new_authorization" = "{name}님,\n{time}, {date}.{day}에 새 기기에서 회원님의 계정 로그인이 감지되었습니다. \n\n기기: {device}\n위치: {location}\n\n본인의 접속이 아니라면 '설정' 창에서 '모든 세션 종료' 기능을 실행하세요.\n\n만약 강제접속 의심이 되신다면 2단계 인증을 설정 - 개인정보 및 보안에서 설정할 수 있습니다\n\n감사합니다.\n\n텔레그램 팀\n";
"lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}";
"lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상";
"lng_new_version7026" = "— 메시지 전달시 코멘트 추가 기능\n— 신규 메시지 및 검색시 기존 사용한 해시태그 제안 기능\n— 원본 메시지 확인 후 기존 답글로 돌아오기 기능\n— 아이디로 그룹 초대 기능";
"lng_new_version_text" = "— 스티커 팩 기능\n— 신규 이모티콘 및 스티커 패널";
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";

View File

@@ -98,6 +98,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_intro_next" = "VOLGENDE";
"lng_intro_finish" = "AANMELDEN";
"lng_intro_submit" = "VERSTUREN";
"lng_phone_ph" = "Je telefoonnummer";
"lng_phone_title" = "Je telefoon";
@@ -124,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "Kan afbeelding zo niet versturen.\nAls bestand versturen?";
"lng_signin_title" = "Cloud-wachtwoord:";
"lng_signin_desc" = "Cloud-wachtwoord invoeren";
"lng_signin_recover_desc" = "Voer de code uit de e-mail in";
"lng_signin_password" = "Je cloud-wachtwoord";
"lng_signin_code" = "Code uit de e-mail";
"lng_signin_recover" = "Wachtwoord vergeten?";
"lng_signin_hint" = "Hint: {password_hint}";
"lng_signin_recover_hint" = "De code is verstuurd naar {recover_email}";
"lng_signin_bad_password" = "Ongeldig wachtwoord.";
"lng_signin_wrong_code" = "Je hebt een ongeldige code ingevoerd. Probeer het opnieuw.";
"lng_signin_try_password" = "Kun je je e-mail niet benaderen?";
"lng_signin_password_removed" = "Je cloud-wachtwoord is uitgeschakeld.\nVia instellingen kun je een nieuwe instellen.";
"lng_signin_no_email_forgot" = "Omdat je geen herstel-e-mailadres hebt opgegeven voor je wachtwoord zul je bij verlies van je wachtwoord je account moeten resetten.";
"lng_signin_cant_email_forgot" = "Als je geen toegang meer hebt tot je e-mail en je ingestelde wachtwoord zul je je account moeten resetten.";
"lng_signin_reset_account" = "Account resetten";
"lng_sigin_sure_reset" = "Let op:\n\nAl je chats, berichten en alle andere data \ngaan verloren als je verder gaat!\n\nEcht je account resetten?";
"lng_sigin_reset" = "Reset";
"lng_signup_title" = "Informatie en foto";
"lng_signup_desc" = "Voer je naam en\nupload een foto.";
@@ -229,6 +248,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "Geavanceerd";
"lng_passcode_remove_button" = "Verwijder";
"lng_passcode_turn_on" = "Toegangscode inschakelen";
"lng_passcode_change" = "Toegangscode wijzigen";
"lng_passcode_create" = "Toegangscode instellen";
@@ -240,16 +261,41 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minute|# minuten}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# uur|# uur}";
"lng_passcode_enter_old" = "Toegangscode invoeren";
"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, tik erop 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";
"lng_passcode_enter" = "Voer je Telegram toegangscode in";
"lng_passcode_enter" = "Toegangscode invoeren";
"lng_passcode_submit" = "Versturen";
"lng_passcode_logout" = "Uitloggen";
"lng_cloud_password_waiting" = "Bevestigingslink verstuurd naar {email}..";
"lng_cloud_password_change" = "Cloud-wachtwoord wijzigen";
"lng_cloud_password_create" = "Cloud-wachtwoord instellen";
"lng_cloud_password_remove" = "Cloud-wachtwoord verwijderen";
"lng_cloud_password_set" = "Twee-staps-verificatie inschakelen";
"lng_cloud_password_edit" = "Cloud-wachtwoord wijzigen";
"lng_cloud_password_enter_old" = "Oud wachtwoord invoeren";
"lng_cloud_password_enter_first" = "Wachtwoord invoeren";
"lng_cloud_password_enter_new" = "Nieuw wachtwoord invoeren";
"lng_cloud_password_confirm_new" = "Wachtwoord opnieuw invoeren";
"lng_cloud_password_hint" = "Wachtwoordhint invoeren";
"lng_cloud_password_bad" = "De hint moet anders zijn dan je wachtwoord.";
"lng_cloud_password_email" = "Herstel-e-mailadres invoeren";
"lng_cloud_password_bad_email" = "Ongeldig e-mailadres, probeer een andere.";
"lng_cloud_password_about" = "Naast de code die je per SMS ontvangt kun je een extra wachtwoord instellen voor als je inlogt op een nieuw apparaat.";
"lng_cloud_password_about_recover" = "Let op: Echt geen herstel-emailadres\nopgeven voor je wachtwoord?\n\nBij verlies van je wachtwoord ben je\nook de toegang tot Telegram kwijt.";
"lng_cloud_password_almost" = "Een bevestigingslink is naar\nhet e-mailadres verstuurd.\n\ntwee-staps-verificatie is actief\nna het klikken van de e-mail-link.";
"lng_cloud_password_was_set" = "Twee-staps-verificatie ingeschakeld.";
"lng_cloud_password_updated" = "Je cloud-wachtwoord is gewijzigd.";
"lng_cloud_password_removed" = "Twee-staps-verificatie uitgeschakeld.";
"lng_cloud_password_differ" = "Wachtwoorden komen niet overeen";
"lng_cloud_password_wrong" = "Onjuist cloud-wachtwoord";
"lng_cloud_password_is_same" = "Wachtwoord is niet gewijzigd";
"lng_connection_type" = "Verbindingstype:";
"lng_connection_auto_connecting" = "Standaard (verbinden)";
"lng_connection_auto" = "Standaard ({type} wordt gebruikt)";
@@ -264,8 +310,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_connection_user_ph" = "Gebruikersnaam";
"lng_connection_password_ph" = "Wachtwoord";
"lng_connection_save" = "Opslaan";
"lng_settings_show_sessions" = "Alle sessies weergeven";
"lng_settings_reset" = "Beëindig alle andere sessies";
"lng_settings_reset_sure" = "Alle apparaten behalve het huidige apparaat uitloggen?";
"lng_settings_reset_sure" = "Alle apparaten behalve het huidige\napparaat uitloggen?";
"lng_settings_reset_one_sure" = "Deze sessie beëindigen?";
"lng_settings_reset_button" = "Beëindigen";
"lng_settings_reset_done" = "Alle andere sessies zijn beëindigd";
"lng_settings_logout" = "Uitloggen";
@@ -275,6 +323,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "Herstarten";
"lng_settings_restart_later" = "Later";
"lng_sessions_header" = "Huidige sessie";
"lng_sessions_other_header" = "Actieve sessies";
"lng_sessions_no_other" = "Geen andere sessies";
"lng_sessions_other_desc" = "Je kunt in Telegram inloggen vanaf andere\napparaten (mobiel,tablet,desktop) met\nhetzelfde telefoonnummer. Al je data\nzal direct worden gesynchroniseerd.";
"lng_sessions_terminate_all" = "Beëindig alle";
"lng_preview_loading" = "Link-preview ophalen...";
"lng_profile_chat_unaccessible" = "Groep is ontoegankelijk";
"lng_topbar_info" = "Info";
"lng_profile_settings_section" = "Instellingen";
@@ -320,20 +376,31 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_sure_delete_and_exit" = "Wil je de groep «{group}» verlaten en de geschiedenis wissen?\n\nDeze actie kan niet ongedaan worden gemaakt.";
"lng_sure_enable_debug" = "Wil je DEBUG-mode inschakelen?\n\nAlle netwerkgebeurtenissen zullen worden gelogd.";
"lng_message_empty" = "(leeg)";
"lng_action_add_user" = "{from} heeft {user} toegevoegd";
"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";
@@ -353,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";
@@ -404,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";
@@ -473,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}";
@@ -487,11 +580,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "Het bestand is opgeslagen in je [c]Downloads[/c]-map";
"lng_new_authorization" = "{name},\nEr is op je account ingelogd vanaf een nieuw apparaat op {day}, {date} om {time}\n\nApparaat: {device}\nLocatie: {location}\n\nAls jij dit niet was, kun je alle sessies beëindigen via Instellingen Beëindig alle andere sessies.\n\nBedankt,\nHet Telegram-Team";
"lng_new_authorization" = "{name},\nEr is op je account ingelogd vanaf een nieuw apparaat op {day}, {date} om {time}\n\nApparaat: {device}\nLocatie: {location}\n\nAls jij dit niet was, kun je alle sessies beëindigen via Instellingen Alle sessies weergeven en de sessie beëindigen.\n\nAls je dat denkt dat iemand anders zonder jouw toestemming is ingelogd kun je twee-staps-verificatie activeren via instellingen.\n\nBedankt,\nHet Telegram-Team";
"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_version7026" = "— Voeg commentaar toe alvorens berichten door te sturen.\n— Hashtag suggesties in nieuwe berichten en zoekvelden (op basis van recente zoekacties).\n— Knop om terug te gaan naar een antwoord na het bekijken van het originele bericht.\n— Voeg mensen toe aan groepen op basis van de gebruikersnaam.";
"lng_new_version_text" = "— Ondersteuning voor stickerbundels toegevoegd\n— Nieuw emoji- en stickerspaneel";
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";

View File

@@ -98,6 +98,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_intro_next" = "PRÓXIMO";
"lng_intro_finish" = "CADASTRAR";
"lng_intro_submit" = "ENVIAR";
"lng_phone_ph" = "Seu número de telefone";
"lng_phone_title" = "Seu Telefone";
@@ -124,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "Essa imagem não pode ser enviada\nassim. Enviar como arquivo?";
"lng_signin_title" = "Verificar senha";
"lng_signin_desc" = "Por favor, insira sua senha.";
"lng_signin_recover_desc" = "Por favor insira o código enviado por e-mail.";
"lng_signin_password" = "Sua senha";
"lng_signin_code" = "Código do e-mail";
"lng_signin_recover" = "Esqueceu sua senha?";
"lng_signin_hint" = "Dica: {password_hint}";
"lng_signin_recover_hint" = "Código enviado para {recover_email}";
"lng_signin_bad_password" = "Você colocou uma senha errada.";
"lng_signin_wrong_code" = "Você colocou um código inválido. Tente novamente.";
"lng_signin_try_password" = "Problemas ao acessar seu e-mail?";
"lng_signin_password_removed" = "Sua senha foi desativada.\nVocê pode definir uma nova em Configurações.";
"lng_signin_no_email_forgot" = "Se você não providenciar um e-mail\nde recuperação ao configurar sua senha, suas opções restantes serão lembrar sua senha ou apagar sua conta.";
"lng_signin_cant_email_forgot" = "Se você não pode restaurar o acesso ao e-mail, suas opções restantes serão lembrar sua senha ou apagar sua conta.";
"lng_signin_reset_account" = "Apagar sua conta";
"lng_sigin_sure_reset" = "Atenção!\n\nVocê irá perder todos os seus chats e mensagens,\njuntamente com qualquer mídias e arquivos compartilhados!\n\nVocê deseja apagar sua conta?";
"lng_sigin_reset" = "Apagar";
"lng_signup_title" = "Informação e foto";
"lng_signup_desc" = "Por favor, insira nome e\ncarregue uma foto.";
@@ -229,10 +248,12 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "Avançado";
"lng_passcode_turn_on" = "Ativar senha";
"lng_passcode_change" = "Alterar senha";
"lng_passcode_create" = "Criar senha";
"lng_passcode_remove" = "Remover senha";
"lng_passcode_remove_button" = "Remover";
"lng_passcode_turn_on" = "Ativar senha de bloqueio";
"lng_passcode_change" = "Alterar senha de bloqueio";
"lng_passcode_create" = "Criar senha de bloqueio";
"lng_passcode_remove" = "Remover senha de bloqueio";
"lng_passcode_turn_off" = "Desativar";
"lng_passcode_autolock" = "Auto-bloquear";
"lng_passcode_autolock_away" = "Auto-bloquear se ausente por:";
@@ -240,16 +261,41 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuto|# minutos}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# hora|# horas}";
"lng_passcode_enter_old" = "Insira sua senha atual";
"lng_passcode_enter_first" = "Insira uma senha";
"lng_passcode_enter_new" = "Insira sua nova senha";
"lng_passcode_confirm_new" = "Re-insira sua nova senha";
"lng_passcode_about" = "Quando você define uma senha adicional, um ícone de cadeado aparece acima. Clique para bloquear o app.\n\nNota: caso esqueça sua senha, terá de relogar no Telegram Desktop.";
"lng_passcode_about" = "Quando uma senha de bloqueio é definida, um ícone de cadeado aparece acima. Clique para bloquear o app.\n\nNota: caso esqueça sua senha, terá de relogar no Telegram Desktop.";
"lng_passcode_differ" = "As senhas não correspondem";
"lng_passcode_wrong" = "Senha errada";
"lng_passcode_is_same" = "Senha não alterada";
"lng_passcode_enter" = "Insira sua senha do Telegram";
"lng_passcode_enter" = "Insira sua senha de bloqueio";
"lng_passcode_submit" = "Entrar";
"lng_passcode_logout" = "Sair";
"lng_cloud_password_waiting" = "Link de confirmação enviado para {email}..";
"lng_cloud_password_change" = "Alterar senha";
"lng_cloud_password_create" = "Criar senha";
"lng_cloud_password_remove" = "Remover senha";
"lng_cloud_password_set" = "Ativar verificação em duas etapas";
"lng_cloud_password_edit" = "Alterar senha";
"lng_cloud_password_enter_old" = "Insira sua senha atual";
"lng_cloud_password_enter_first" = "Insira uma senha";
"lng_cloud_password_enter_new" = "Insira sua nova senha";
"lng_cloud_password_confirm_new" = "Re-insira sua nova senha";
"lng_cloud_password_hint" = "Insira uma dica de senha";
"lng_cloud_password_bad" = "Senha e dica de senha não podem ser iguais.";
"lng_cloud_password_email" = "E-mail de recuperação";
"lng_cloud_password_bad_email" = "E-mail incorreto, por favor, tente outro.";
"lng_cloud_password_about" = "Essa senha será requisitada quando você entrar em um novo dispositivo, após o código via SMS.";
"lng_cloud_password_about_recover" = "Atenção! Você tem certeza que não quer\nadicionar um email para recuperação de senha?\n\nSe você esquecer sua senha, perderá\nseu acesso a sua conta no Telegram.";
"lng_cloud_password_almost" = "Link de confirmação enviado\npara o email fornecido.\n\nA verificação em duas etapas será\nativada assim que você abrir o link.";
"lng_cloud_password_was_set" = "Verificação em duas etapas ativada.";
"lng_cloud_password_updated" = "Sua senha foi atualizada.";
"lng_cloud_password_removed" = "Verificação em duas etapas desativada.";
"lng_cloud_password_differ" = "As senhas não correspondem.";
"lng_cloud_password_wrong" = "Senha errada";
"lng_cloud_password_is_same" = "Senha não alterada";
"lng_connection_type" = "Tipo de conexão:";
"lng_connection_auto_connecting" = "Padrão (conectando..)";
"lng_connection_auto" = "Padrão ({type} usado)";
@@ -264,8 +310,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_connection_user_ph" = "Nome de usuário";
"lng_connection_password_ph" = "Senha";
"lng_connection_save" = "Salvar";
"lng_settings_reset" = "Encerrar outras sessões";
"lng_settings_reset_sure" = "Tem certeza que deseja encerrar todas as outras sessões?";
"lng_settings_show_sessions" = "Exibir todas as sessões";
"lng_settings_reset" = "Encerrar todas as outras sessões";
"lng_settings_reset_sure" = "Você tem certeza que deseja terminar\ntodas as outras sessões?";
"lng_settings_reset_one_sure" = "Você deseja terminar essa sessão?";
"lng_settings_reset_button" = "Encerrar";
"lng_settings_reset_done" = "Outras sessões encerradas";
"lng_settings_logout" = "Sair";
@@ -275,10 +323,18 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "Reiniciar";
"lng_settings_restart_later" = "Depois";
"lng_sessions_header" = "Sessão atual";
"lng_sessions_other_header" = "Sessões ativas";
"lng_sessions_no_other" = "Nenhuma outra sessão";
"lng_sessions_other_desc" = "Você pode entrar no Telegram de outro\ncelular, tablet e computadores, usando\no mesmo número de telefone. Todos seus\ndados estarão sincronizados instantaneamente.";
"lng_sessions_terminate_all" = "Encerrar tudo";
"lng_preview_loading" = "Obtendo informações..";
"lng_profile_chat_unaccessible" = "Grupo inacessível";
"lng_topbar_info" = "Info";
"lng_profile_settings_section" = "Configurações";
"lng_profile_participants_section" = "Participantes";
"lng_profile_participants_section" = "Membros";
"lng_profile_info" = "Informação de contato";
"lng_profile_group_info" = "Informação do grupo";
"lng_profile_add_contact" = "Adicionar Contato";
@@ -315,12 +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_enable_debug" = "Você deseja habilitar o modo DEBUG?\n\nTodos os eventos da rede serão reportados.";
"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)";
@@ -328,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";
@@ -353,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";
@@ -404,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";
@@ -473,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}";
@@ -487,11 +580,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "Imagem foi salva na sua pasta [c]Downloads[/c]";
"lng_new_authorization" = "{name},\nDetectamos um acesso à sua conta de um novo dispositivo em {day}, {date} às {time}\n\nDispositivo: {device}\nLocalização: {location}\n\nCaso não tenha sido você, vá em Configurações Terminar todas as outras sessões.\n\nObrigado,\nEquipe Telegram";
"lng_new_authorization" = "{name},\nDetectamos um acesso à sua conta de um novo dispositivo em {day}, {date} às {time}\n\nDispositivo: {device}\nLocalização: {location}\n\nCaso não tenha sido você, vá em Configurações Mostrar todas as sessões e terminar essa sessão.\n\nSe você acha que alguém entrou em sua conta, você pode ativar a verificação em duas etapas nas Configurações.\n\nObrigado,\nEquipe Telegram";
"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_version7026" = "— Adicione um comentário antes das mensagens encaminhadas\n— Sugestões de hashtags na nova mensagem e campos de busca (baseados nas buscas recentes)\n— Botão para voltar para a resposta depois de visualizar a mensagem original\n— Adicione pessoas em grupos pelo nome de usuário";
"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";

View File

@@ -178,7 +178,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 +188,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");
}

View File

@@ -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);
@@ -1777,7 +1888,7 @@ namespace Local {
_writeMap(WriteMapFast);
}
EncryptedDescriptor data(sizeof(quint64) + _stringSize(draft.text) + sizeof(qint32));
data.stream << quint64(peer) << draft.text << qint32(draft.replyTo);
data.stream << quint64(peer) << draft.text << qint32(draft.replyTo) << qint32(draft.previewCancelled ? 1 : 0);
FileWriteDescriptor file(i.value());
file.writeEncrypted(data);
@@ -1801,10 +1912,11 @@ namespace Local {
quint64 draftPeer;
QString draftText;
qint32 draftReplyTo = 0;
qint32 draftReplyTo = 0, draftPreviewCancelled = 0;
draft.stream >> draftPeer >> draftText;
if (draft.version >= 7021) draft.stream >> draftReplyTo;
return (draftPeer == peer) ? MessageDraft(MsgId(draftReplyTo), draftText) : MessageDraft();
if (draft.version >= 8001) draft.stream >> draftPreviewCancelled;
return (draftPeer == peer) ? MessageDraft(MsgId(draftReplyTo), draftText, (draftPreviewCancelled == 1)) : MessageDraft();
}
void writeDraftPositions(const PeerId &peer, const MessageCursor &cur) {
@@ -1924,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();
@@ -1980,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() {
@@ -1991,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();
@@ -2011,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();
}
@@ -2036,7 +2148,7 @@ namespace Local {
}
int32 hasStickers() {
return _stickersMap.size();
return _stickerImagesMap.size();
}
qint64 storageStickersSize() {
@@ -2095,56 +2207,91 @@ namespace Local {
return _storageAudiosSize;
}
void writeRecentStickers() {
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 << quint32(sets.size()) << cStickersHash();
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
if (i->stickers.isEmpty()) 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 << quint64(i->id) << quint64(i->access) << i->title << i->shortName << quint32(i->stickers.size());
for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) {
DocumentData *doc = *j;
data.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: {
data.stream << qint32(StickerSetTypeID);
} break;
case mtpc_inputStickerSetShortName: {
data.stream << qint32(StickerSetTypeShortName);
} break;
case mtpc_inputStickerSetEmpty:
default: {
data.stream << qint32(StickerSetTypeEmpty);
} break;
}
const StorageImageLocation &loc(doc->sticker->loc);
data.stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret);
}
}
FileWriteDescriptor file(_recentStickersKey);
FileWriteDescriptor file(_stickersKey);
file.writeEncrypted(data);
}
}
void readRecentStickers() {
if (!_recentStickersKey) return;
void importOldRecentStickers() {
if (!_recentStickersKeyOld) return;
FileReadDescriptor stickers;
if (!readEncryptedFile(stickers, _recentStickersKey)) {
clearKey(_recentStickersKey);
_recentStickersKey = 0;
if (!readEncryptedFile(stickers, _recentStickersKeyOld)) {
clearKey(_recentStickersKeyOld);
_recentStickersKeyOld = 0;
_writeMap();
return;
}
StickerSets &sets(cRefStickerSets());
sets.clear();
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;
@@ -2154,7 +2301,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;
@@ -2162,23 +2309,122 @@ 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)));
}
if (def.stickers.isEmpty()) sets.remove(DefaultStickerSetId);
if (custom.stickers.isEmpty()) sets.remove(CustomStickerSetId);
writeStickers();
writeUserSettings();
clearKey(_recentStickersKeyOld);
_recentStickersKeyOld = 0;
_writeMap();
}
void readStickers() {
if (!_stickersKey) {
return importOldRecentStickers();
}
cSetRecentStickers(recent);
FileReadDescriptor stickers;
if (!readEncryptedFile(stickers, _stickersKey)) {
clearKey(_stickersKey);
_stickersKey = 0;
_writeMap();
return;
}
StickerSets &sets(cRefStickerSets());
sets.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);
}
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);
}
void writeBackground(int32 id, const QImage &img) {
if (!_working()) return;
QByteArray png;
{
if (!img.isNull()) {
QBuffer buf(&png);
if (!img.save(&buf, "BMP")) return;
}
@@ -2187,9 +2433,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);
@@ -2209,7 +2456,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);
@@ -2329,8 +2586,8 @@ namespace Local {
_storageImagesSize = 0;
_mapChanged = true;
}
if (!_stickersMap.isEmpty()) {
_stickersMap.clear();
if (!_stickerImagesMap.isEmpty()) {
_stickerImagesMap.clear();
_storageStickersSize = 0;
_mapChanged = true;
}
@@ -2351,8 +2608,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) {
@@ -2379,9 +2640,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;
@@ -2389,8 +2650,8 @@ namespace Local {
data->stickers.insert(k, i.value());
}
}
if (!_stickersMap.isEmpty()) {
_stickersMap.clear();
if (!_stickerImagesMap.isEmpty()) {
_stickerImagesMap.clear();
_storageStickersSize = 0;
_mapChanged = true;
}

View File

@@ -101,10 +101,11 @@ namespace Local {
int32 oldMapVersion();
struct MessageDraft {
MessageDraft(MsgId replyTo = 0, QString text = QString()) : replyTo(replyTo), text(text) {
MessageDraft(MsgId replyTo = 0, QString text = QString(), bool previewCancelled = false) : replyTo(replyTo), text(text), previewCancelled(previewCancelled) {
}
MsgId replyTo;
QString text;
bool previewCancelled;
};
void writeDraft(const PeerId &peer, const MessageDraft &draft);
MessageDraft readDraft(const PeerId &peer);
@@ -121,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();
@@ -131,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();

View File

@@ -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();
}

View File

@@ -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()));
@@ -376,6 +377,10 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
connect(audioVoice(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
connect(audioVoice(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
}
connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
_webPageUpdater.setSingleShot(true);
connect(&_webPageUpdater, SIGNAL(timeout()), this, SLOT(webPagesUpdate()));
connect(&_cacheBackgroundTimer, SIGNAL(timeout()), this, SLOT(onCacheBackground()));
@@ -516,6 +521,43 @@ void MainWidget::finishForwarding(History *hist) {
history.peerMessagesUpdated(hist->peer->id);
}
void MainWidget::webPageUpdated(WebPageData *data) {
_webPagesUpdated.insert(data->id, true);
_webPageUpdater.start(0);
}
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()));
if (j != items.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
itemResized(k.key());
}
}
}
_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);
}
@@ -683,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) {
@@ -887,7 +929,7 @@ DialogsIndexed &MainWidget::contactsList() {
return dialogs.contactsList();
}
void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo) {
void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo, WebPageId webPageId) {
saveRecentHashtags(text);
QString sendingText, leftText = text;
if (replyTo < 0) replyTo = history.replyToId();
@@ -899,9 +941,20 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
MTPstring msgText(MTP_string(sendingText));
int32 flags = (hist->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out);
if (replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
int32 sendFlags = 0;
if (replyTo) {
flags |= MTPDmessage::flag_reply_to_msg_id;
sendFlags |= MTPmessages_SendMessage::flag_reply_to_msg_id;
}
MTPMessageMedia media = MTP_messageMediaEmpty();
if (webPageId == 0xFFFFFFFFFFFFFFFFULL) {
sendFlags |= MTPmessages_SendMessage_flag_skipWebPage;
} else if (webPageId) {
WebPageData *page = App::webPage(webPageId);
media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill)));
}
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media));
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
}
finishForwarding(hist);
@@ -929,7 +982,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentStickers();
Local::readRecentHashtags();
recent = cRecentWriteHashtags();
}
found = true;
@@ -960,6 +1013,7 @@ void MainWidget::stopAnimActive() {
}
void MainWidget::searchMessages(const QString &query) {
App::wnd()->hideMediaview();
dialogs.searchMessages(query);
if (!cWideMode()) onShowDialogs();
}
@@ -1105,6 +1159,11 @@ void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
void MainWidget::itemResized(HistoryItem *row, bool scrollToIt) {
if (!row || (history.peer() == row->history()->peer && !row->detached())) {
history.itemResized(row, scrollToIt);
} else if (row) {
row->history()->width = 0;
if (history.peer() == row->history()->peer) {
history.resizeEvent(0);
}
}
if (overview) {
overview->itemResized(row, scrollToIt);
@@ -1157,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();
}
}
@@ -1238,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);
}
@@ -1369,7 +1429,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
if (reader.supportsAnimation() && reader.imageCount() > 1 && item) {
startGif(item, already);
} else {
App::wnd()->showDocument(document, QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly), item);
App::wnd()->showDocument(document, item);
}
} else {
psOpenFile(already);
@@ -1392,6 +1452,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) {
@@ -1409,6 +1470,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);
}
@@ -1552,6 +1645,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());
}
@@ -1879,11 +1974,9 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
}
}
if (d.vmedia.type() != mtpc_messageMediaEmpty) {
HistoryItem *item = App::histItemById(d.vid.v);
if (item) {
item->setMedia(d.vmedia);
}
HistoryItem *item = App::histItemById(d.vid.v);
if (item) {
item->setMedia(d.vmedia);
}
} break;
@@ -2260,6 +2353,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: {
@@ -2337,6 +2431,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();
}
}
@@ -2362,6 +2457,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) {
@@ -2380,7 +2479,7 @@ void MainWidget::start(const MTPUser &user) {
}
_started = true;
App::wnd()->sendServiceHistoryRequest();
Local::readRecentStickers();
Local::readStickers();
history.start();
}
@@ -2389,13 +2488,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) {
@@ -2408,6 +2522,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));
@@ -2427,6 +2558,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?..
@@ -2449,7 +2657,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;
}
@@ -2457,16 +2667,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();
}
@@ -2479,11 +2688,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);
}
@@ -2524,46 +2735,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));
@@ -2572,11 +2785,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();
}
@@ -2707,6 +2954,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 {
@@ -2768,7 +3016,8 @@ void MainWidget::handleUpdates(const MTPUpdates &updates) {
case mtpc_updateShortMessage: {
const MTPDupdateShortMessage &d(updates.c_updateShortMessage());
if (!App::userLoaded(d.vuser_id.v)) {
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)) {
@@ -2786,7 +3035,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)) {
@@ -2802,6 +3054,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;
}
@@ -2859,13 +3112,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: {
@@ -2890,15 +3151,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updateWebPage: {
const MTPDupdateWebPage &d(update.c_updateWebPage());
WebPageData *page = App::feedWebPage(d.vwebpage);
const WebPageItems &items(App::webPageItems());
WebPageItems::const_iterator i = items.constFind(page);
if (i != items.cend()) {
for (HistoryItemsMap::const_iterator j = i.value().cbegin(), e = i.value().cend(); j != e; ++j) {
j.key()->initDimensions();
itemResized(j.key());
}
}
App::feedWebPage(d.vwebpage);
history.updatePreview();
webPagesUpdate();
} break;
case mtpc_updateDeleteMessages: {

View File

@@ -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);
@@ -283,7 +287,7 @@ public:
DialogsIndexed &contactsList();
void sendMessage(History *history, const QString &text, MsgId replyTo);
void sendPreparedText(History *hist, const QString &text, MsgId replyTo);
void sendPreparedText(History *hist, const QString &text, MsgId replyTo, WebPageId webPageId = 0);
void saveRecentHashtags(const QString &text);
void readServerHistory(History *history, bool force = true);
@@ -333,6 +337,15 @@ 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();
signals:
@@ -344,9 +357,12 @@ signals:
void dialogToTop(const History::DialogLinks &links);
void dialogsUpdated();
void showPeerAsync(quint64 peer, qint32 msgId, bool back, bool force);
void stickersUpdated();
public slots:
void webPagesUpdate();
void videoLoadProgress(mtpFileLoader *loader);
void videoLoadFailed(mtpFileLoader *loader, bool started);
void videoLoadRetry();
@@ -363,6 +379,7 @@ public slots:
void onParentResize(const QSize &newSize);
void getDifference();
void mtpPing();
void getDifferenceForce();
void updateOnline(bool gotOtherOffline = false);
@@ -387,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;
@@ -405,6 +428,11 @@ private:
Text _toForwardFrom, _toForwardText;
int32 _toForwardNameVersion;
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);
@@ -422,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();

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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,10 @@ with open('scheme.tl') as f:
size = [];
for k in prmsList:
v = prms[k];
size.append('v' + k + '.innerLength()');
if (k in conditionsList):
size.append('(has_' + k + '() ? v' + k + '.innerLength() : 0)');
else:
size.append('v' + k + '.innerLength()');
if (not len(size)):
size.append('0');
funcsText += '\t\treturn ' + ' + '.join(size) + ';\n';
@@ -221,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';
@@ -230,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';
@@ -266,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);
@@ -275,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;
@@ -289,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';
@@ -308,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);
@@ -352,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;
@@ -393,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
@@ -448,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)');
@@ -479,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

View File

@@ -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() % _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();
}
};

View File

@@ -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"

View File

@@ -1096,7 +1096,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
, firstSentAt(-1)
, _pingId(0)
, _pingIdToSend(0)
, _pingSent(0)
, _pingSendAt(0)
, _pingMsgId(0)
, restarted(false)
, keyId(0)
@@ -1114,6 +1114,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
// createConn();
if (!dc) {
QReadLocker lock(mtpDcOptionsMutex());
const mtpDcOptions &options(cDcOptions());
if (options.isEmpty()) {
LOG(("MTP Error: connect failed, no DCs"));
@@ -1137,6 +1138,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
connect(this, SIGNAL(needToReceive()), sessionData->owner(), SLOT(tryToReceive()), Qt::QueuedConnection);
connect(this, SIGNAL(stateChanged(qint32)), sessionData->owner(), SLOT(onConnectionStateChange(qint32)), Qt::QueuedConnection);
connect(sessionData->owner(), SIGNAL(needToSend()), this, SLOT(tryToSend()), Qt::QueuedConnection);
connect(sessionData->owner(), SIGNAL(needToPing()), this, SLOT(onPingSendForce()), Qt::QueuedConnection);
connect(this, SIGNAL(sessionResetDone()), sessionData->owner(), SLOT(onResetDone()), Qt::QueuedConnection);
static bool _registered = false;
@@ -1425,7 +1427,7 @@ void MTProtoConnectionPrivate::tryToSend() {
bool prependOnly = (state != MTProtoConnection::Connected);
mtpRequest pingRequest;
if (dc < _mtp_internal::dcShift) { // main session
if (!prependOnly && !_pingIdToSend && !_pingId && _pingSent + (MTPPingSendAfterAuto * 1000ULL) <= getms(true)) {
if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= getms(true)) {
_pingIdToSend = MTP::nonce<mtpPingId>();
}
}
@@ -1444,7 +1446,8 @@ void MTProtoConnectionPrivate::tryToSend() {
DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, ping_id: %1").arg(_pingIdToSend));
}
_pingSent = pingRequest->msDate = getms(true); // > 0 - can send without container
pingRequest->msDate = getms(true); // > 0 - can send without container
_pingSendAt = pingRequest->msDate + (MTPPingSendAfterAuto * 1000ULL);
pingRequest->requestId = 0; // dont add to haveSent / wereAcked maps
if (dc < _mtp_internal::dcShift && !prependOnly) { // main session
@@ -1711,6 +1714,8 @@ void MTProtoConnectionPrivate::retryByTimer() {
}
if (keyId == mtpAuthKey::RecreateKeyId) {
if (sessionData->getKey()) {
unlockKey();
QWriteLocker lock(sessionData->keyMutex());
sessionData->owner()->destroyKey();
}
@@ -1733,34 +1738,37 @@ void MTProtoConnectionPrivate::socketStart(bool afterConfig) {
return;
}
setState(MTProtoConnection::Connecting);
_pingId = _pingMsgId = _pingIdToSend = _pingSent = 0;
_pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0;
_pingSender.stop();
const mtpDcOption *dcOption = 0;
const mtpDcOptions &options(cDcOptions());
mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift);
DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc));
if (dcIndex == options.cend()) {
std::string ip;
uint32 port = 0;
{
QReadLocker lock(mtpDcOptionsMutex());
const mtpDcOptions &options(cDcOptions());
mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift);
DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc));
if (dcIndex != options.cend()) {
ip = dcIndex->ip;
port = dcIndex->port;
}
}
if (!port || ip.empty()) {
if (afterConfig) {
LOG(("MTP Error: DC %1 options not found right after config load!").arg(dc));
return restart();
} else {
DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc));
connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded()));
mtpConfigLoader()->load();
return;
}
DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc));
connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded()));
mtpConfigLoader()->load();
return;
}
dcOption = &dcIndex.value();
const char *ip(dcOption->ip.c_str());
uint32 port(dcOption->port);
DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip).arg(port));
DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip.c_str()).arg(port));
connect(conn, SIGNAL(connected()), this, SLOT(onConnected()));
connect(conn, SIGNAL(disconnected()), this, SLOT(restart()));
conn->connectToServer(ip, port);
conn->connectToServer(ip.c_str(), port);
}
void MTProtoConnectionPrivate::restart(bool maybeBadKey) {
@@ -1840,17 +1848,25 @@ void MTProtoConnectionPrivate::onOldConnection() {
void MTProtoConnectionPrivate::onPingSender() {
if (_pingId) {
if (_pingSent + (MTPPingSendAfter - 1) * 1000 < getms(true)) {
if (_pingSendAt + (MTPPingSendAfter - MTPPingSendAfterAuto - 1) * 1000ULL < getms(true)) {
LOG(("Could not send ping for MTPPingSendAfter seconds, restarting.."));
return restart();
} else {
_pingSender.start(_pingSent + (MTPPingSendAfter * 1000) - getms(true));
_pingSender.start(_pingSendAt + (MTPPingSendAfter - MTPPingSendAfterAuto) * 1000ULL - getms(true));
}
} else {
emit needToSendAsync();
}
}
void MTProtoConnectionPrivate::onPingSendForce() {
if (!_pingId) {
_pingSendAt = 0;
DEBUG_LOG(("Will send ping!"));
tryToSend();
}
}
void MTProtoConnectionPrivate::onBadConnection() {
if (cConnectionType() != dbictAuto && cConnectionType() != dbictTcpProxy) {
return;
@@ -2493,6 +2509,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
case mtpc_new_session_created: {
if (badTime) return 0;
const mtpPrime *start = from;
MTPNewSession msg(from, end);
const MTPDnew_session_created &data(msg.c_new_session_created());
DEBUG_LOG(("Message Info: new server session created, unique_id %1, first_msg_id %2, server_salt %3").arg(data.vunique_id.v).arg(data.vfirst_msg_id.v).arg(data.vserver_salt.v));
@@ -2511,8 +2528,8 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
}
resendMany(toResend, 10, true);
mtpBuffer update(end - from);
if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime));
mtpBuffer update(from - start);
if (from > start) memcpy(update.data(), start, (from - start) * sizeof(mtpPrime));
QWriteLocker locker(sessionData->haveReceivedMutex());
mtpResponseMap &haveReceived(sessionData->haveReceivedMap());
@@ -2813,12 +2830,36 @@ void MTProtoConnectionPrivate::onConnected() {
TCP_LOG(("Connection Info: connection succeed."));
if (updateAuthKey()) {
DEBUG_LOG(("MTP Info: returning from socketConnected.."));
return;
updateAuthKey();
}
void MTProtoConnectionPrivate::updateAuthKey() {
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData || !conn) return;
DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc));
uint64 newKeyId = 0;
{
ReadLockerAttempt lock(sessionData->keyMutex());
if (!lock) {
DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit"));
clearMessages();
keyId = newKeyId;
return; // some other connection is getting key
}
const mtpAuthKeyPtr &key(sessionData->getKey());
newKeyId = key ? key->keyId() : 0;
}
if (keyId != newKeyId) {
clearMessages();
keyId = newKeyId;
}
DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str()));
if (keyId) {
return authKeyCreated();
}
DEBUG_LOG(("MTP Info: will be creating auth_key"));
DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key"));
lockKey();
const mtpAuthKeyPtr &key(sessionData->getKey());
@@ -2843,36 +2884,6 @@ void MTProtoConnectionPrivate::onConnected() {
sendRequestNotSecure(req_pq);
}
bool MTProtoConnectionPrivate::updateAuthKey() {
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData || !conn) return false;
DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc));
uint64 newKeyId = 0;
{
ReadLockerAttempt lock(sessionData->keyMutex());
if (!lock) {
DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit"));
clearMessages();
keyId = newKeyId;
return true; // some other connection is getting key
}
const mtpAuthKeyPtr &key(sessionData->getKey());
newKeyId = key ? key->keyId() : 0;
}
if (keyId != newKeyId) {
clearMessages();
keyId = newKeyId;
}
DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str()));
if (keyId) {
authKeyCreated();
return true;
}
DEBUG_LOG(("AuthKey Info: Key update failed"));
return false;
}
void MTProtoConnectionPrivate::clearMessages() {
if (keyId && keyId != mtpAuthKey::RecreateKeyId && conn) {
conn->received().clear();
@@ -3470,7 +3481,14 @@ MTProtoConnectionPrivate::~MTProtoConnectionPrivate() {
void MTProtoConnectionPrivate::stop() {
QWriteLocker lockFinished(&sessionDataMutex);
sessionData = 0;
if (sessionData) {
if (myKeyLock) {
sessionData->owner()->notifyKeyCreated(mtpAuthKeyPtr()); // release key lock, let someone else create it
sessionData->keyMutex()->unlock();
myKeyLock = false;
}
sessionData = 0;
}
}
MTProtoConnection::~MTProtoConnection() {

View File

@@ -24,6 +24,8 @@ enum {
MTPDmessage_flag_unread = (1 << 0),
MTPDmessage_flag_out = (1 << 1),
MTPDmessage_flag_notify_by_from = (1 << 4),
MTPDmessage_flag_media_unread = (1 << 5),
MTPmessages_SendMessage_flag_skipWebPage = (1 << 1),
};
#include "mtproto/mtpPublicRSA.h"
@@ -340,6 +342,7 @@ public slots:
void restart(bool maybeBadKey = false);
void onPingSender();
void onPingSendForce();
void onBadConnection();
void onOldConnection();
void onSentSome(uint64 size);
@@ -363,7 +366,7 @@ public slots:
// Sessions signals, when we need to send something
void tryToSend();
bool updateAuthKey();
void updateAuthKey();
void onConfigLoaded();
@@ -415,7 +418,7 @@ private:
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
mtpPingId _pingId, _pingIdToSend;
uint64 _pingSent;
uint64 _pingSendAt;
mtpMsgId _pingMsgId;
SingleTimer _pingSender;

View File

@@ -366,7 +366,7 @@ static const mtpTypeId mtpLayers[] = {
mtpc_invokeWithLayer17,
mtpc_invokeWithLayer18,
}, mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]);
static const mtpPrime mtpCurrentLayer = 27;
static const mtpPrime mtpCurrentLayer = 29;
template <typename bareT>
class MTPBoxed : public bareT {

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