Compare commits

...

30 Commits

Author SHA1 Message Date
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
John Preston
11dd70cb1a version 0.8.1.dev prepared 2015-04-05 21:11:28 +03:00
John Preston
406fc2a4d4 sessions, two-step verification and links preview done for linux version 2015-04-05 11:49:03 +03:00
John Preston
e6ef982706 sessions, two-step verification and links preview done for os x version 2015-04-05 11:40:56 +03:00
John Preston
f3bb155b0a links preview done 2015-04-04 23:01:34 +03:00
John Preston
868d5f60f3 cloud password support added, sessions list added, some boxes unifications 2015-04-02 13:33:19 +03:00
John Preston
7814654d38 stable version 0.8 prepared 2015-03-26 13:45:47 +03:00
John Preston
1b67494e0b Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-03-26 12:35:34 +03:00
John Preston
e052a11326 0.7.26.dev - some bugs fixed 2015-03-26 12:35:21 +03:00
John Preston
75bfc6b893 langs updated, right click in profile fixed 2015-03-26 12:32:57 +03:00
John Preston
8c7a35c973 add users to groups by usernames, copy username from context menu in profile, 0.7.25.dev version 2015-03-25 18:42:15 +03:00
John Preston
489b151d49 os x and linux dialog path remember through executions 2015-03-24 18:49:07 +03:00
John Preston
b5d3580150 removing of recent hashtags added 2015-03-24 18:18:20 +03:00
John Preston
08e283bc91 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-03-24 14:11:59 +03:00
John Preston
07463dda6b improved 0.7.24.dev, changelog added 2015-03-24 14:11:38 +03:00
John Preston
4f7cc79b4c warning fixed 2015-03-24 13:21:30 +03:00
John Preston
938707203c 0.7.24.dev version with hashtags autocomplete, forwarding with comment and move back to reply by bottom arrow 2015-03-24 13:00:27 +03:00
John Preston
69879332fd non clickable mention in changelog 2015-03-20 09:56:42 +03:00
John Preston
01ad967640 version 0.7.23 stable is ready 2015-03-20 09:41:11 +03:00
John Preston
d8c853bb83 langs updated 2015-03-20 09:38:22 +03:00
John Preston
db6ed90337 0.7.22.dev version with fixed messages delivery 2015-03-20 00:56:13 +03:00
John Preston
8991b5ead3 langs updated 2015-03-20 00:49:44 +03:00
John Preston
a301866560 0.7.22 changelog 2015-03-19 23:08:14 +03:00
John Preston
75cad6179d changelog to 0.7.21.dev added 2015-03-19 14:40:47 +03:00
John Preston
2f094c2350 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop 2015-03-19 14:25:12 +03:00
John Preston
8d614463ae 0.7.21.dev version prepared for os x 2015-03-19 14:24:23 +03:00
John Preston
1f7e39e184 version 0.7.21.dev prepared - replies, mentions 2015-03-19 12:18:19 +03:00
John Preston
39acdd8725 layer 25 support started 2015-03-13 16:01:25 +03:00
145 changed files with 18753 additions and 12181 deletions

View File

@@ -1,10 +1,10 @@
@echo OFF
set "AppVersion=7020"
set "AppVersionStrSmall=0.7.20"
set "AppVersionStr=0.7.20"
set "AppVersionStrFull=0.7.20.0"
set "DevChannel=0"
set "AppVersion=8002"
set "AppVersionStrSmall=0.8.2"
set "AppVersionStr=0.8.2"
set "AppVersionStrFull=0.8.2.0"
set "DevChannel=1"
if %DevChannel% neq 0 goto preparedev

View File

@@ -89,14 +89,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_chat_status_members_online" = "{count:_not_used_|# member|# members}, {count_online:_not_used_|# online|# online}";
"lng_server_error" = "Internal server error.";
"lng_flood_error" = "Too much tries. Please try again later.";
"lng_flood_error" = "Too many tries. Please try again later.";
"lng_deleted" = "Unknown";
"lng_deleted_message" = "Deleted message";
"lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b].";
"lng_start_msgs" = "START MESSAGING";
"lng_intro_next" = "NEXT";
"lng_intro_finish" = "SIGN UP";
"lng_intro_submit" = "SUBMIT";
"lng_phone_ph" = "Your phone number";
"lng_phone_title" = "Your Phone";
@@ -123,6 +125,24 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_bad_image_for_photo" = "This image can't be sent that way.\nWould you like to send it as a file?";
"lng_signin_title" = "Cloud password check";
"lng_signin_desc" = "Please enter your cloud password.";
"lng_signin_recover_desc" = "Please enter the code from the e-mail.";
"lng_signin_password" = "Your cloud password";
"lng_signin_code" = "Code from e-mail";
"lng_signin_recover" = "Forgot password?";
"lng_signin_hint" = "Hint: {password_hint}";
"lng_signin_recover_hint" = "Code was sent to {recover_email}";
"lng_signin_bad_password" = "You have entered a wrong password.";
"lng_signin_wrong_code" = "You have entered an invalid code. Please try again.";
"lng_signin_try_password" = "Having trouble accessing your e-mail?";
"lng_signin_password_removed" = "Your cloud password was disabled.\nYou can set a new one in Settings.";
"lng_signin_no_email_forgot" = "Since you haven't provided a recovery\ne-mail when setting up your password, your remaining options are either to remember your password or to reset your account.";
"lng_signin_cant_email_forgot" = "If you can't restore access to the e-mail, your remaining options are either to remember your password or to reset your account.";
"lng_signin_reset_account" = "Reset your account";
"lng_sigin_sure_reset" = "Warning!\n\nYou will lose all your chats and messages,\nalong with any media and files you shared!\n\nDo you want to reset your account?";
"lng_sigin_reset" = "Reset";
"lng_signup_title" = "Information and photo";
"lng_signup_desc" = "Please enter your name and\nupload a photo.";
@@ -228,10 +248,12 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_advanced" = "Advanced";
"lng_passcode_turn_on" = "Turn passcode on";
"lng_passcode_change" = "Change passcode";
"lng_passcode_create" = "Create passcode";
"lng_passcode_remove" = "Remove passcode";
"lng_passcode_remove_button" = "Remove";
"lng_passcode_turn_on" = "Turn on local passcode";
"lng_passcode_change" = "Change local passcode";
"lng_passcode_create" = "Create local passcode";
"lng_passcode_remove" = "Remove local passcode";
"lng_passcode_turn_off" = "Turn off";
"lng_passcode_autolock" = "Auto-Lock";
"lng_passcode_autolock_away" = "Auto-Lock if away for:";
@@ -239,16 +261,41 @@ 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 passcode is set, a lock icon appears in the top menu. Click it to lock the app.\n\nNote: if you forget your passcode, you'll need to relogin in Telegram Desktop.";
"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.";
"lng_passcode_differ" = "Passcodes are different";
"lng_passcode_wrong" = "Wrong passcode";
"lng_passcode_is_same" = "Passcode was not changed";
"lng_passcode_enter" = "Enter your Telegram Passcode";
"lng_passcode_enter" = "Enter your local passcode";
"lng_passcode_submit" = "Submit";
"lng_passcode_logout" = "Log out";
"lng_cloud_password_waiting" = "Confirmation link sent to {email}..";
"lng_cloud_password_change" = "Change cloud password";
"lng_cloud_password_create" = "Create cloud password";
"lng_cloud_password_remove" = "Remove cloud password";
"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";
"lng_cloud_password_bad" = "Password and hint cannot be the same.";
"lng_cloud_password_email" = "Enter recovery e-mail";
"lng_cloud_password_bad_email" = "Incorrect e-mail, please try other.";
"lng_cloud_password_about" = "This password will be required when you log in on a new device in addition to the pin code.";
"lng_cloud_password_about_recover" = "Warning! Are you sure you don't want to\nadd a password recovery e-mail?\n\nIf you forget your password, you will\nlose access to your Telegram account.";
"lng_cloud_password_almost" = "A confirmation link was sent\nto the e-mail you provided.\n\nTwo-step verification will be enabled\nas soon as you follow that link.";
"lng_cloud_password_was_set" = "Two-step verification enabled.";
"lng_cloud_password_updated" = "Your cloud password was updated.";
"lng_cloud_password_removed" = "Two-step verification was disabled.";
"lng_cloud_password_differ" = "Passwords do not match";
"lng_cloud_password_wrong" = "Wrong cloud password";
"lng_cloud_password_is_same" = "Password was not changed";
"lng_connection_type" = "Connection type:";
"lng_connection_auto_connecting" = "Default (connecting..)";
"lng_connection_auto" = "Default ({type} used)";
@@ -263,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" = "Save";
"lng_settings_reset" = "Terminate other sessions";
"lng_settings_reset_sure" = "Are you sure you want to terminate all other sessions?";
"lng_settings_show_sessions" = "Show all sessions";
"lng_settings_reset" = "Terminate all other sessions";
"lng_settings_reset_sure" = "Are you sure you want to terminate\nall other sessions?";
"lng_settings_reset_one_sure" = "Do you want to terminate this session?";
"lng_settings_reset_button" = "Terminate";
"lng_settings_reset_done" = "Other sessions terminated";
"lng_settings_logout" = "Log Out";
@@ -274,10 +323,18 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_restart_now" = "Restart";
"lng_settings_restart_later" = "Later";
"lng_sessions_header" = "Current session";
"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";
"lng_profile_participants_section" = "Participants";
"lng_profile_participants_section" = "Members";
"lng_profile_info" = "Contact info";
"lng_profile_group_info" = "Group info";
"lng_profile_add_contact" = "Add Contact";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Next";
"lng_create_group_title" = "New Group";
"lng_failed_add_participant" = "Could not add user. Try again later.";
"lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
"lng_sure_delete_and_exit" = "Are you sure, you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
"lng_sure_enable_debug" = "Do you want to enable DEBUG mode?\n\nAll network events will be logged.";
"lng_message_empty" = "(empty)";
"lng_action_add_user" = "{from} added {user}";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} created group «{title}»";
"lng_forwarded_from" = "Forwarded from";
"lng_in_reply_to" = "In reply to";
"lng_attach_failed" = "Failed";
"lng_attach_file" = "File";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "Copy email address";
"lng_context_open_hashtag" = "Search by hashtag";
"lng_context_copy_hashtag" = "Copy hashtag";
"lng_context_open_mention" = "Open profile";
"lng_context_copy_mention" = "Copy username";
"lng_context_open_image" = "Open Image";
"lng_context_save_image" = "Save Image As..";
"lng_context_forward_image" = "Forward Image";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Forward File";
"lng_context_delete_file" = "Delete File";
"lng_context_close_file" = "Close File";
"lng_context_copy_text" = "Copy Message Text";
"lng_context_copy_text" = "Copy Text";
"lng_context_to_msg" = "Go To Message";
"lng_context_reply_msg" = "Reply";
"lng_context_forward_msg" = "Forward Message";
"lng_context_delete_msg" = "Delete Message";
"lng_context_select_msg" = "Select Message";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "Send selected files to {recipient}?";
"lng_forward" = "Forward";
"lng_forward_send" = "Send";
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
"lng_forwarding_from" = "{user} and {count:_not_used_|# other|# others}";
"lng_forwarding_from_two" = "{user} and {second_user}";
"lng_contact_phone" = "Phone number";
"lng_enter_contact_data" = "New Contact";
@@ -477,12 +541,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "Image was saved to your [c]Downloads[/c] folder";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Terminate other sessions.\n\nThanks,\nThe Telegram Team";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team";
"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_version7020" = "— Lock your app with a passcode";
"lng_new_version7020_appstore" = "— Lock your app with a passcode\n— Change the chat background\n— New «open with» menu for files\n— Added Korean language";
"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_menu_insert_unicode" = "Insert Unicode control character";

View File

@@ -376,6 +376,7 @@ introAlphaShowFunc: transition(easeInCirc);
introTextTop: 22px;
introTextSize: size(400px, 93px);
introCallSkip: 15px;
introPwdTextSize: size(400px, 73px);
btnIntroSep: 12px;
btnIntroNext: flatButton(btnDefNext, btnDefBig) {
@@ -426,9 +427,11 @@ inpIntroCode: flatInput(inpDefGray) {
inpIntroName: flatInput(inpIntroPhone) {
width: 192px;
}
inpIntroPassword: flatInput(inpIntroPhone) {
width: 300px;
}
introSelectDelta: 30px;
introSelectMaxHeight: 550px;
btnSelectDone: flatButton(btnDefFlat) {
color: btnYesColor;
overColor: btnYesHover;
@@ -455,6 +458,16 @@ btnSelectCancel: flatButton(btnSelectDone) {
downColor: btnNoHover;
}
btnSelectSep: #e0e0e0;
btnRedLink: linkButton(btnDefLink) {
color: #d15948;
overColor: #d15948;
downColor: #db6352;
}
btnRedDone: flatButton(btnSelectDone) {
color: #d15948;
overColor: #d15948;
downColor: #db6352;
}
countryList: countryList {
notFoundColor: #aaa;//rgb(20, 136, 210);
@@ -489,8 +502,7 @@ countriesAlphaShowFunc: transition(easeInCirc);
introErrWidth: 450px;
introErrDuration: 200;
introErrFunc: transition(linear);
introErrBG: #ffa5a5;
introErrColor: black;//#800000;
introErrColor: black;
introErrTop: 15px;
introErrHeight: 40px;
introErrFont: font(16px);
@@ -679,7 +691,7 @@ dlgHistFont: font(fsize);
dlgNameColor: #000;
dlgNameTop: 2px;
dlgSystemColor: #4981af;
dlgTextColor: #888888;
dlgTextColor: #888;
dlgDateFont: font(13px);
dlgDateColor: #a8a8a8;
@@ -784,6 +796,8 @@ msgInSelectOverlay: #358cd44c;
msgStickerOverlay: #358cd47f;
msgOutServiceColor: #3a8e26;
msgInServiceColor: #0e7acd;
msgOutServiceSelColor: #367570;
msgInServiceSelColor: #0e7acd;
msgShadow: 2px;
msgInShadow: #748ea229;
msgOutShadow: #3ac34740;
@@ -794,6 +808,15 @@ msgOutDateColor: #6cc264;
msgInSelectDateColor: #6a9cc5;
msgOutSelectDateColor: #50a79c;
msgReplyPadding: margins(6px, 6px, 11px, 6px);
msgReplyBarPos: point(1px, 0px);
msgReplyBarSize: size(2px, 36px);
msgReplyBarSkip: 10px;
msgOutReplyBarColor: #5dc452;
msgInReplyBarColor: #2fa9e2;
msgOutReplyBarSelColor: #4da79f;
msgInReplyBarSelColor: #2fa9e2;
msgServiceSelectBG: #fff4;
msgServiceRadius: 2px;
@@ -812,6 +835,7 @@ msgBG: ':/gui/art/bg.png' / 2:':/gui/art/bg_125x.png' / 3:':/gui/art/bg_150x.png
msgSendingRect: sprite(260px, 20px, 20px, 20px);
msgCheckRect: sprite(320px, 0px, 20px, 20px);
msgCheckPos: point(5px, 1px);
msgDblCheckRect: sprite(300px, 0px, 20px, 20px);
msgSelectCheckRect: sprite(160px, 0px, 20px, 20px);
msgSelectDblCheckRect: sprite(140px, 0px, 20px, 20px);
@@ -878,7 +902,7 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) {
mediaMaxWidth: 250px;
mediaFont: font(fsize);
mediaPadding: margins(7px, 6px, 11px, 6px);
mediaPadding: margins(7px, 6px, 7px, 6px);
mediaThumbSize: 48px;
mediaNameTop: 3px;
mediaDetailsShift: 3px;
@@ -960,6 +984,25 @@ btnAttachEmoji: iconedButton(btnAttachDocument) {
width: 32px;
}
replySkip: 51px;
replyColor: #377aae;
replyHeight: 49px;
replyTop: 8px;
replyBottom: 6px;
replyIconPos: point(13px, 13px);
replyIcon: sprite(174px, 195px, 24px, 24px);
replyCancel: iconedButton(btnDefIconed) {
icon: sprite(165px, 24px, 14px, 14px);
iconPos: point(17px, 17px);
downIcon: sprite(165px, 24px, 14px, 14px);
downIconPos: point(17px, 18px);
bgColor: white;
overBgColor: white;
width: 49px;
height: 49px;
}
forwardIcon: sprite(368px, 173px, 24px, 24px);
historyScroll: flatScroll(scrollDef) {
barColor: #89a0b47a;
bgColor: #89a0b44c;
@@ -1001,18 +1044,22 @@ layerPadding: margins(10px, 10px, 10px, 10px);
boxFont: font(16px);
boxPadding: margins(18px, 18px, 18px, 18px);
boxBG: white;//rgb(228, 233, 240);
boxVerticalMargin: 10px;
boxWidth: 364px;
boxMaxListHeight: 600px;
boxBG: white;
boxGrayTitle: #777;
confirmWidth: 364px;
boxTitlePos: point(20px, 15px);
boxTitleFont: font(17px);
boxTitleHeight: 52px;
confirmMaxHeight: 320px;
confirmCompressedSkip: 10px;
addContactWidth: 364px;
addContactPadding: margins(18px, 24px, 18px, 24px);
addContactDelta: 14px;
addContactTitleHeight: 52px;
addContactTitlePos: point(20px, 15px);
addContactTitleFont: font(17px);
inpAddContact: flatInput(inpDefGray) {
height: 42px;
textMrg: margins(10px, 5px, 10px, 5px);
@@ -1169,10 +1216,13 @@ profileNameInput: flatInput(setNameInput) {
width: 230px;
}
newGroupScroll: flatScroll(scrollDef) {
topsh: 0px;
boxScroll: flatScroll(scrollDef) {
topsh: -2px;
bottomsh: -2px;
}
boxNoTopScroll: flatScroll(boxScroll) {
topsh: 0;
}
participantInnerAdd: flatButton(btnDefNext) {
width: 145px;
@@ -1188,8 +1238,6 @@ participantInnerCancel: flatButton(participantInnerAdd, btnDefBack) {
participantCancel: flatButton(participantInnerAdd, btnDefBack) {
width: 300px;
}
participantWidth: 364px;
participantMaxHeight: 600px;
participantFilter: flatInput(inpDefFlat) {
width: 364px;
height: 52px;
@@ -1301,7 +1349,7 @@ aboutCloseButton: flatButton(contactsClose) {
downTextTop: 16px;
}
btnInfoClose: flatButton(aboutCloseButton) {
width: confirmWidth;
width: boxWidth;
}
emojiTextFont: font(16px);
@@ -1318,6 +1366,7 @@ dropdownShadow: sprite(241px, 46px, 6px, 6px);
dropdownBorder: 1px;
dropdownBorderColor: #ebebeb;
dropdownBackground: white;
dropdownDuration: 150;
dropdownAttachDocument: iconedButton(btnAttachDocument) {
iconPos: point(14px, 13px);
@@ -1341,15 +1390,15 @@ dropdownMediaPhotos: iconedButton(dropdownAttachPhoto) {
width: 200px;
}
dropdownMediaVideos: iconedButton(dropdownMediaPhotos) {
icon: sprite(79px, 348px, 24px, 24px);
downIcon: sprite(79px, 348px, 24px, 24px);
icon: sprite(92px, 348px, 24px, 24px);
downIcon: sprite(92px, 348px, 24px, 24px);
}
dropdownMediaDocuments: iconedButton(dropdownAttachDocument) {
width: 200px;
}
dropdownMediaAudios: iconedButton(dropdownMediaDocuments) {
icon: sprite(49px, 348px, 24px, 24px);
downIcon: sprite(49px, 348px, 24px, 24px);
icon: sprite(62px, 348px, 24px, 24px);
downIcon: sprite(62px, 348px, 24px, 24px);
}
dragFont: font(28px semibold);
@@ -1683,6 +1732,7 @@ usernameCancel: flatButton(btnSelectCancel) {
youtubeIcon: sprite(336px, 221px, 60px, 60px);
vimeoIcon: sprite(336px, 283px, 60px, 60px);
videoIcon: sprite(0px, 340px, 60px, 60px);
locationSize: size(320, 240);
langsWidth: 220px;
@@ -1697,9 +1747,6 @@ langsCloseButton: flatButton(aboutCloseButton) {
backgroundPadding: 10px;
backgroundSize: size(108px, 193px);
backgroundScroll: flatScroll(newGroupScroll) {
topsh: -2px;
}
passcodeHeaderFont: font(19px);
passcodeHeaderHeight: 80px;
@@ -1714,3 +1761,41 @@ passcodeSubmit: flatButton(btnIntroNext) {
font: font(19px);
overFont: font(19px);
}
mentionHeight: 40px;
mentionScroll: flatScroll(scrollDef) {
topsh: 0;
bottomsh: 0;
}
mentionPadding: margins(8px, 5px, 8px, 5px);
mentionTop: 11px;
mentionFont: linkFont;
mentionPhotoSize: msgPhotoSize;
sessionsHeight: 440px;
sessionHeight: 70px;
sessionPadding: margins(20px, 10px, 20px, 0);
sessionsCloseButton: flatButton(aboutCloseButton) {
width: boxWidth;
}
sessionNameFont: msgNameFont;
sessionActiveFont: msgDateFont;
sessionActiveColor: #aaa;
sessionInfoFont: msgFont;
sessionInfoColor: dlgTextColor;
sessionTerminateTop: 30px;
sessionTerminateSkip: 10px;
sessionTerminate: iconedButton(notifyClose) {
iconPos: point(3px, 3px);
downIconPos: point(3px, 4px);
width: 16px;
height: 16px;
}
webPageLeft: 10px;
webPageBar: 2px;
webPageTitleFont: font(fsize semibold);
webPageDescriptionFont: font(fsize);
webPagePhotoSkip: 5px;
webPagePhotoSize: 100px;
webPagePhotoDelta: 8px;

View File

@@ -314,7 +314,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
LPWSTR *args;
int argsCount;
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false;
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, testmode = false;
args = CommandLineToArgvW(GetCommandLine(), &argsCount);
if (args) {
for (int i = 1; i < argsCount; ++i) {
@@ -327,6 +327,8 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
openLog();
} else if (equal(args[i], L"-startintray")) {
startintray = true;
} else if (equal(args[i], L"-testmode")) {
testmode = true;
} else if (equal(args[i], L"-writeprotected") && ++i < argsCount) {
writeprotected = true;
updateTo = args[i];
@@ -376,6 +378,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
if (autostart) targs += L" -autostart";
if (debug) targs += L" -debug";
if (startintray) targs += L" -startintray";
if (testmode) targs += L" -testmode";
bool executed = false;
if (writeprotected) { // run un-elevated

View File

@@ -303,7 +303,7 @@ int main(int argc, char *argv[]) {
writeLog("Updater started..");
bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false;
bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false, testmode = false;
char *key = 0;
for (int i = 1; i < argc; ++i) {
@@ -316,6 +316,8 @@ int main(int argc, char *argv[]) {
openLog();
} else if (equal(argv[i], "-startintray")) {
startintray = true;
} else if (equal(argv[i], "-testmode")) {
testmode = true;
} else if (equal(argv[i], "-tosettings")) {
tosettings = true;
} else if (equal(argv[i], "-key") && ++i < argc) {
@@ -379,13 +381,14 @@ int main(int argc, char *argv[]) {
char path[MaxLen] = {0};
strcpy(path, (exeDir + "Telegram").c_str());
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_startintray[] = "-startintray";
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_startintray[] = "-startintray", p_testmode[] = "-testmode";
int argIndex = 0;
args[argIndex++] = path;
args[argIndex++] = p_noupdate;
if (autostart) args[argIndex++] = p_autostart;
if (debug) args[argIndex++] = p_debug;
if (startintray) args[argIndex++] = p_startintray;
if (testmode) args[argIndex++] = p_testmode;
if (tosettings) args[argIndex++] = p_tosettings;
if (key) {
args[argIndex++] = p_key;

View File

@@ -78,7 +78,7 @@ int main(int argc, const char * argv[]) {
openLog();
pid_t procId = 0;
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO;
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, testMode = NO;
NSString *key = nil;
for (int i = 0; i < argc; ++i) {
if ([@"-workpath" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
@@ -101,6 +101,8 @@ int main(int argc, const char * argv[]) {
_debug = YES;
} else if ([@"-startintray" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
startInTray = YES;
} else if ([@"-testmode" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
testMode = YES;
} else if ([@"-key" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
if (++i < argc) key = [NSString stringWithUTF8String:argv[i]];
}
@@ -194,6 +196,7 @@ int main(int argc, const char * argv[]) {
if (toSettings) [args addObject:@"-tosettings"];
if (_debug) [args addObject:@"-debug"];
if (startInTray) [args addObject:@"-startintray"];
if (testMode) [args addObject:@"-testmode"];
if (autoStart) [args addObject:@"-autostart"];
if (key) {
[args addObject:@"-key"];

View File

@@ -0,0 +1,286 @@
/*
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 "style.h"
#include "lang.h"
#include "application.h"
#include "window.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "localstorage.h"
ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
App::initBackground();
connect(&_replyToTimer, SIGNAL(timeout()), this, SLOT(resolveReplyTo()));
connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages()));
}
void ApiWrap::init() {
App::initMedia();
}
void ApiWrap::itemRemoved(HistoryItem *item) {
if (HistoryReply *reply = item->toHistoryReply()) {
ReplyToRequests::iterator i = _replyToRequests.find(reply->replyToId());
if (i != _replyToRequests.cend()) {
for (QList<HistoryReply*>::iterator j = i->replies.begin(); j != i->replies.end();) {
if ((*j) == reply) {
j = i->replies.erase(j);
} else {
++j;
}
}
if (i->replies.isEmpty()) {
_replyToRequests.erase(i);
}
}
}
}
void ApiWrap::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (HistoryReply *reply = oldItem->toHistoryReply()) {
ReplyToRequests::iterator i = _replyToRequests.find(reply->replyToId());
if (i != _replyToRequests.cend()) {
for (QList<HistoryReply*>::iterator j = i->replies.begin(); j != i->replies.end();) {
if ((*j) == reply) {
if (HistoryReply *newReply = newItem->toHistoryReply()) {
*j = newReply;
++j;
} else {
j = i->replies.erase(j);
}
} else {
++j;
}
}
if (i->replies.isEmpty()) {
_replyToRequests.erase(i);
}
}
}
}
void ApiWrap::requestReplyTo(HistoryReply *reply, MsgId to) {
ReplyToRequest &req(_replyToRequests[to]);
req.replies.append(reply);
if (!req.req) _replyToTimer.start(1);
}
void ApiWrap::requestFullPeer(PeerData *peer) {
if (_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));
} else {
req = MTP::send(MTPusers_GetFullUser(peer->asUser()->inputUser), rpcDone(&ApiWrap::gotUserFull, peer), rpcFail(&ApiWrap::gotPeerFailed, peer));
}
_fullRequests.insert(peer, req);
}
void ApiWrap::requestWebPageDelayed(WebPageData *page) {
if (page->pendingTill <= 0) return;
_webPagesPending.insert(page, 0);
int32 left = (page->pendingTill - unixtime()) * 1000;
if (!_webPagesTimer.isActive() || left <= _webPagesTimer.remainingTime()) {
_webPagesTimer.start((left < 0 ? 0 : left) + 1);
}
}
void ApiWrap::clearWebPageRequest(WebPageData *page) {
_webPagesPending.remove(page);
if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) _webPagesTimer.stop();
}
void ApiWrap::clearWebPageRequests() {
_webPagesPending.clear();
_webPagesTimer.stop();
}
void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
const MTPDmessages_chatFull &d(result.c_messages_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) {
chat->photoId = photo->id;
photo->chat = chat;
}
}
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vfull_chat.c_chatFull().vnotify_settings);
_fullRequests.remove(peer);
emit fullPeerLoaded(peer);
}
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
const MTPDuserFull &d(result.c_userFull());
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
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);
_fullRequests.remove(peer);
emit fullPeerLoaded(peer);
}
bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_fullRequests.remove(peer);
return true;
}
void ApiWrap::resolveReplyTo() {
if (_replyToRequests.isEmpty()) return;
QVector<MTPint> ids;
ids.reserve(_replyToRequests.size());
for (ReplyToRequests::const_iterator i = _replyToRequests.cbegin(), e = _replyToRequests.cend(); i != e; ++i) {
if (!i.value().req) {
ids.push_back(MTP_int(i.key()));
}
}
if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotReplyTo));
for (ReplyToRequests::iterator i = _replyToRequests.begin(), e = _replyToRequests.end(); i != e; ++i) {
i.value().req = req;
}
}
}
void ApiWrap::resolveWebPages() {
QVector<MTPint> ids;
const WebPageItems &items(App::webPageItems());
ids.reserve(_webPagesPending.size());
int32 t = unixtime(), m = INT_MAX;
for (WebPagesPending::const_iterator i = _webPagesPending.cbegin(), e = _webPagesPending.cend(); i != e; ++i) {
if (i.value()) continue;
if (i.key()->pendingTill <= t) {
WebPageItems::const_iterator j = items.constFind(i.key());
if (j != items.cend() && !j.value().isEmpty()) {
ids.push_back(MTP_int(j.value().begin().key()->id));
}
} else {
m = qMin(m, i.key()->pendingTill - t);
}
}
if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotWebPages));
for (WebPagesPending::iterator i = _webPagesPending.begin(); i != _webPagesPending.cend(); ++i) {
if (i.value()) continue;
if (i.key()->pendingTill <= t) {
i.value() = req;
}
}
}
if (m < INT_MAX) _webPagesTimer.start(m * 1000);
}
void ApiWrap::gotReplyTo(const MTPmessages_Messages &msgs, mtpRequestId req) {
switch (msgs.type()) {
case mtpc_messages_messages:
App::feedUsers(msgs.c_messages_messages().vusers);
App::feedChats(msgs.c_messages_messages().vchats);
App::feedMsgs(msgs.c_messages_messages().vmessages, -1);
break;
case mtpc_messages_messagesSlice:
App::feedUsers(msgs.c_messages_messagesSlice().vusers);
App::feedChats(msgs.c_messages_messagesSlice().vchats);
App::feedMsgs(msgs.c_messages_messagesSlice().vmessages, -1);
break;
}
for (ReplyToRequests::iterator i = _replyToRequests.begin(); i != _replyToRequests.cend();) {
if (i.value().req == req) {
for (QList<HistoryReply*>::const_iterator j = i.value().replies.cbegin(), e = i.value().replies.cend(); j != e; ++j) {
if (*j) {
(*j)->updateReplyTo(true);
} else {
App::main()->updateReplyTo();
}
}
i = _replyToRequests.erase(i);
} else {
++i;
}
}
}
void ApiWrap::gotWebPages(const MTPmessages_Messages &msgs, mtpRequestId req) {
const QVector<MTPMessage> *v = 0;
switch (msgs.type()) {
case mtpc_messages_messages:
App::feedUsers(msgs.c_messages_messages().vusers);
App::feedChats(msgs.c_messages_messages().vchats);
v = &msgs.c_messages_messages().vmessages.c_vector().v;
break;
case mtpc_messages_messagesSlice:
App::feedUsers(msgs.c_messages_messagesSlice().vusers);
App::feedChats(msgs.c_messages_messagesSlice().vchats);
v = &msgs.c_messages_messagesSlice().vmessages.c_vector().v;
break;
}
QMap<int32, int32> msgsIds; // copied from feedMsgs
for (int32 i = 0, l = v->size(); i < l; ++i) {
const MTPMessage &msg(v->at(i));
switch (msg.type()) {
case mtpc_message: msgsIds.insert(msg.c_message().vid.v, i); break;
case mtpc_messageEmpty: msgsIds.insert(msg.c_messageEmpty().vid.v, i); break;
case mtpc_messageService: msgsIds.insert(msg.c_messageService().vid.v, i); break;
}
}
MainWidget *m = App::main();
for (QMap<int32, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
HistoryItem *item = App::histories().addToBack(v->at(*i), -1);
if (item) {
item->initDimensions();
if (m) m->itemResized(item);
}
}
const WebPageItems &items(App::webPageItems());
for (WebPagesPending::iterator i = _webPagesPending.begin(); i != _webPagesPending.cend();) {
if (i.value() == req) {
if (i.key()->pendingTill > 0) {
i.key()->pendingTill = -1;
WebPageItems::const_iterator j = items.constFind(i.key());
if (j != items.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());
}
}
}
i = _webPagesPending.erase(i);
} else {
++i;
}
}
}
ApiWrap::~ApiWrap() {
App::deinitMedia(false);
}

View File

@@ -0,0 +1,74 @@
/*
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
class ApiWrap : public QObject, public RPCSender {
Q_OBJECT
public:
ApiWrap(QObject *parent);
void init();
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void requestReplyTo(HistoryReply *reply, MsgId to);
void requestFullPeer(PeerData *peer);
void requestWebPageDelayed(WebPageData *page);
void clearWebPageRequest(WebPageData *page);
void clearWebPageRequests();
~ApiWrap();
signals:
void fullPeerLoaded(PeerData *peer);
public slots:
void resolveReplyTo();
void resolveWebPages();
private:
void gotReplyTo(const MTPmessages_Messages &result, mtpRequestId req);
struct ReplyToRequest {
ReplyToRequest() : req(0) {
}
mtpRequestId req;
QList<HistoryReply*> replies;
};
typedef QMap<MsgId, ReplyToRequest> ReplyToRequests;
ReplyToRequests _replyToRequests;
SingleTimer _replyToTimer;
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result);
void gotUserFull(PeerData *peer, const MTPUserFull &result);
bool gotPeerFailed(PeerData *peer, const RPCError &err);
typedef QMap<PeerData*, mtpRequestId> FullRequests;
FullRequests _fullRequests;
void gotWebPages(const MTPmessages_Messages &result, mtpRequestId req);
typedef QMap<WebPageData*, mtpRequestId> WebPagesPending;
WebPagesPending _webPagesPending;
SingleTimer _webPagesTimer;
};

View File

@@ -49,9 +49,15 @@ namespace {
typedef QHash<DocumentId, DocumentData*> DocumentsData;
DocumentsData documentsData;
typedef QHash<WebPageId, WebPageData*> WebPagesData;
WebPagesData webPagesData;
VideoItems videoItems;
AudioItems audioItems;
DocumentItems documentItems;
WebPageItems webPageItems;
typedef QMap<HistoryItem*, QMap<HistoryReply*, bool> > RepliesTo;
RepliesTo repliesTo;
Histories histories;
@@ -118,6 +124,10 @@ namespace App {
return app() ? app()->uploader() : 0;
}
ApiWrap *api() {
return main() ? main()->api() : 0;
}
void showSettings() {
Window *w(wnd());
if (w) w->showSettings();
@@ -303,7 +313,7 @@ namespace App {
const MTPuser &user(*i);
data = 0;
bool wasContact = false;
const MTPUserStatus *status = 0;
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
switch (user.type()) {
case mtpc_userEmpty: {
@@ -315,8 +325,9 @@ namespace App {
data->inputUser = MTP_inputUserContact(d.vid);
data->setName(lang(lng_deleted), QString(), QString(), QString());
data->setPhoto(MTP_userProfilePhotoEmpty());
data->access = 0;
data->access = UserNoAccess;
wasContact = (data->contact > 0);
status = &emptyStatus;
data->contact = -1;
} break;
case mtpc_userDeleted: {
@@ -326,10 +337,12 @@ namespace App {
data = App::user(peer);
data->input = MTP_inputPeerContact(d.vid);
data->inputUser = MTP_inputUserContact(d.vid);
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
data->setName(lang(lng_deleted), QString(), QString(), QString());
// data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
data->setPhoto(MTP_userProfilePhotoEmpty());
data->access = 0;
data->access = UserNoAccess;
wasContact = (data->contact > 0);
status = &emptyStatus;
data->contact = -1;
} break;
case mtpc_userSelf: {
@@ -344,7 +357,6 @@ namespace App {
data->setPhone(qs(d.vphone));
data->access = 0;
wasContact = (data->contact > 0);
data->contact = -1;
status = &d.vstatus;
if (::self != data) {
@@ -369,7 +381,7 @@ namespace App {
} break;
case mtpc_userRequest: {
const MTPDuserRequest &d(user.c_userRequest());
PeerId peer(peerFromUser(d.vid.v));
data = App::user(peer);
data->input = MTP_inputPeerForeign(d.vid, d.vaccess_hash);
@@ -590,20 +602,19 @@ namespace App {
}
}
void feedMsgs(const MTPVector<MTPMessage> &msgs, bool newMsgs) {
void feedMsgs(const MTPVector<MTPMessage> &msgs, int msgsState) {
const QVector<MTPMessage> &v(msgs.c_vector().v);
QMap<int32, int32> msgsIds;
for (int32 i = 0, l = v.size(); i < l; ++i) {
const MTPMessage &msg(v[i]);
const MTPMessage &msg(v.at(i));
switch (msg.type()) {
case mtpc_message: msgsIds.insert(msg.c_message().vid.v, i); break;
case mtpc_messageEmpty: msgsIds.insert(msg.c_messageEmpty().vid.v, i); break;
case mtpc_messageForwarded: msgsIds.insert(msg.c_messageForwarded().vid.v, i); break;
case mtpc_messageService: msgsIds.insert(msg.c_messageService().vid.v, i); break;
}
}
for (QMap<int32, int32>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
histories().addToBack(v[*i], newMsgs ? 1 : 0);
histories().addToBack(v.at(*i), msgsState);
}
}
@@ -646,6 +657,20 @@ namespace App {
}
}
void feedInboxRead(const PeerId &peer, int32 upTo) {
History *h = App::historyLoaded(peer);
if (h) {
h->inboxRead(upTo);
}
}
void feedOutboxRead(const PeerId &peer, int32 upTo) {
History *h = App::historyLoaded(peer);
if (h) {
h->outboxRead(upTo);
}
}
void feedWereDeleted(const QVector<MTPint> &msgsIds) {
bool resized = false;
for (QVector<MTPint>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
@@ -683,30 +708,20 @@ namespace App {
}
}
void feedUserLink(MTPint userId, const MTPcontacts_MyLink &myLink, const MTPcontacts_ForeignLink &foreignLink) {
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink) {
UserData *user = userLoaded(userId.v);
if (user) {
bool wasContact = (user->contact > 0);
switch (myLink.type()) {
case mtpc_contacts_myLinkContact:
case mtpc_contactLinkContact:
user->contact = 1;
break;
case mtpc_contacts_myLinkEmpty:
case mtpc_contacts_myLinkRequested:
if (myLink.type() == mtpc_contacts_myLinkRequested && myLink.c_contacts_myLinkRequested().vcontact.v) {
user->contact = 1;
} else {
switch (foreignLink.type()) {
case mtpc_contacts_foreignLinkRequested:
if (foreignLink.c_contacts_foreignLinkRequested().vhas_phone.v) {
user->contact = 0;
} else {
user->contact = -1;
}
break;
default: user->contact = -1; break;
}
}
case mtpc_contactLinkHasPhone:
user->contact = 0;
break;
case mtpc_contactLinkNone:
case mtpc_contactLinkUnknown:
user->contact = -1;
break;
}
if (user->contact > 0) {
@@ -716,7 +731,7 @@ namespace App {
if (user->inputUser.type() != mtpc_inputUserSelf) user->inputUser = MTP_inputUserContact(userId);
}
} else {
if (user->access) {
if (user->access && user->access != UserNoAccess) {
if (user->input.type() != mtpc_inputPeerSelf) user->input = MTP_inputPeerForeign(userId, MTP_long(user->access));
if (user->inputUser.type() != mtpc_inputUserSelf) user->inputUser = MTP_inputUserForeign(userId, MTP_long(user->access));
}
@@ -732,20 +747,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;
case mtpc_messageForwarded: media = &msg.c_messageForwarded().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: {
@@ -891,6 +892,23 @@ namespace App {
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);
}
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(), 0);
}
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert) {
return App::webPage(webpage.vid.v, convert, QString(), QString(), QString(), QString(), QString(), QString(), 0, 0, QString(), webpage.vdate.v);
}
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_webPagePending: return App::feedWebPage(webpage.c_webPagePending());
}
return 0;
}
UserData *userLoaded(const PeerId &user) {
PeerData *peer = peerLoaded(user);
return (peer && peer->loaded) ? peer->asUser() : 0;
@@ -1151,14 +1169,84 @@ namespace App {
result->thumb = thumb;
result->dc = dc;
result->size = size;
} else if (result->thumb->isNull() && !thumb->isNull()) {
result->thumb = thumb;
} else {
if (result->thumb->isNull() && !thumb->isNull()) {
result->thumb = thumb;
}
if (result->alt.isEmpty()) {
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);
}
}
}
}
}
}
}
return result;
}
WebPageData *webPage(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, int32 duration, const QString &author, int32 pendingTill) {
if (convert) {
if (convert->id != webPage) {
WebPagesData::iterator i = webPagesData.find(convert->id);
if (i != webPagesData.cend() && i.value() == convert) {
webPagesData.erase(i);
}
convert->id = webPage;
}
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) {
convert->type = toWebPageType(type);
convert->url = url;
convert->displayUrl = displayUrl;
convert->siteName = siteName;
convert->title = title;
convert->description = description;
convert->photo = photo;
convert->duration = duration;
convert->author = author;
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
convert->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(convert);
}
}
WebPagesData::const_iterator i = webPagesData.constFind(webPage);
WebPageData *result;
if (i == webPagesData.cend()) {
if (convert) {
result = convert;
} else {
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);
}
}
webPagesData.insert(webPage, result);
} else {
result = i.value();
if (result != convert) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) {
result->type = toWebPageType(type);
result->url = url;
result->displayUrl = displayUrl;
result->siteName = siteName;
result->title = title;
result->description = description;
result->photo = photo;
result->duration = duration;
result->author = author;
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
result->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(result);
}
}
}
return result;
}
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type, const QString &url) {
ImageLinksData::const_iterator i = imageLinksData.constFind(imageLink);
ImageLinkData *result;
@@ -1213,11 +1301,14 @@ namespace App {
return ::histories;
}
History *history(const PeerId &peer, int32 unreadCnt) {
History *history(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead) {
Histories::const_iterator i = ::histories.constFind(peer);
if (i == ::histories.cend()) {
i = App::histories().insert(peer, new History(peer));
i.value()->setUnreadCount(unreadCnt, false);
if (maxInboxRead) {
i.value()->inboxReadTill = maxInboxRead;
}
}
return i.value();
}
@@ -1236,6 +1327,22 @@ namespace App {
}
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (HistoryReply *r = oldItem->toHistoryReply()) {
QMap<HistoryReply*, bool> &replies(::repliesTo[r->replyToMessage()]);
replies.remove(r);
if (HistoryReply *n = newItem->toHistoryReply()) {
replies.insert(n, true);
}
}
RepliesTo::iterator i = ::repliesTo.find(oldItem);
if (i != ::repliesTo.cend() && oldItem != newItem) {
QMap<HistoryReply*, bool> replies = i.value();
::repliesTo.erase(i);
::repliesTo[newItem] = replies;
for (QMap<HistoryReply*, bool>::iterator i = replies.begin(), e = replies.end(); i != e; ++i) {
i.key()->replyToReplaced(oldItem, newItem);
}
}
newItem->history()->itemReplaced(oldItem, newItem);
if (App::main()) App::main()->itemReplaced(oldItem, newItem);
if (App::hoveredItem() == oldItem) App::hoveredItem(newItem);
@@ -1294,6 +1401,13 @@ namespace App {
}
}
historyItemDetached(item);
RepliesTo::iterator j = ::repliesTo.find(item);
if (j != ::repliesTo.cend()) {
for (QMap<HistoryReply*, bool>::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->replyToReplaced(item, 0);
}
::repliesTo.erase(j);
}
if (App::main() && !App::quiting()) {
App::main()->itemRemoved(item);
}
@@ -1337,6 +1451,11 @@ namespace App {
delete *i;
}
documentsData.clear();
for (WebPagesData::const_iterator i = webPagesData.cbegin(), e = webPagesData.cend(); i != e; ++i) {
delete *i;
}
webPagesData.clear();
if (api()) api()->clearWebPageRequests();
cSetRecentStickers(RecentStickerPack());
cSetStickersHash(QByteArray());
cSetStickers(AllStickers());
@@ -1344,12 +1463,29 @@ namespace App {
::videoItems.clear();
::audioItems.clear();
::documentItems.clear();
::webPageItems.clear();
::repliesTo.clear();
lastPhotos.clear();
lastPhotosMap.clear();
::self = 0;
if (App::wnd()) App::wnd()->updateGlobalMenu();
}
/* // don't delete history without deleting its' peerdata
void historyRegReply(HistoryReply *reply, HistoryItem *to) {
::repliesTo[to].insert(reply, true);
}
void historyUnregReply(HistoryReply *reply, HistoryItem *to) {
RepliesTo::iterator i = ::repliesTo.find(to);
if (i != ::repliesTo.cend()) {
i.value().remove(reply);
if (i.value().isEmpty()) {
::repliesTo.erase(i);
}
}
}
/* // don't delete history without deleting its' peerdata
void deleteHistory(const PeerId &peer) {
Histories::iterator i = ::histories.find(peer);
if (i != ::histories.end()) {
@@ -1620,6 +1756,18 @@ namespace App {
return ::documentItems;
}
void regWebPageItem(WebPageData *data, HistoryItem *item) {
::webPageItems[data][item] = true;
}
void unregWebPageItem(WebPageData *data, HistoryItem *item) {
::webPageItems[data].remove(item);
}
const WebPageItems &webPageItems() {
return ::webPageItems;
}
void setProxySettings(QNetworkAccessManager &manager) {
if (cConnectionType() == dbictHttpProxy) {
const ConnectionProxy &p(cConnectionProxy());
@@ -1640,13 +1788,13 @@ namespace App {
void searchByHashtag(const QString &tag) {
if (App::main()) {
App::main()->searchMessages(tag);
App::main()->searchMessages(tag + ' ');
}
}
void openUserByName(const QString &username) {
void openUserByName(const QString &username, bool toProfile) {
if (App::main()) {
App::main()->openUserByName(username);
App::main()->openUserByName(username, toProfile);
}
}

View File

@@ -23,6 +23,7 @@ class Application;
class Window;
class MainWidget;
class SettingsWidget;
class ApiWrap;
class Font;
class Color;
class FileUploader;
@@ -33,6 +34,7 @@ typedef QMap<HistoryItem*, bool> HistoryItemsMap;
typedef QHash<VideoData*, HistoryItemsMap> VideoItems;
typedef QHash<AudioData*, HistoryItemsMap> AudioItems;
typedef QHash<DocumentData*, HistoryItemsMap> DocumentItems;
typedef QHash<WebPageData*, HistoryItemsMap> WebPageItems;
namespace App {
Application *app();
@@ -41,6 +43,7 @@ namespace App {
SettingsWidget *settings();
bool passcoded();
FileUploader *uploader();
ApiWrap *api();
void showSettings();
void logOut();
@@ -74,12 +77,13 @@ namespace App {
void feedParticipants(const MTPChatParticipants &p);
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
void feedMsgs(const MTPVector<MTPMessage> &msgs, bool newMsgs = false);
void feedMsgs(const MTPVector<MTPMessage> &msgs, int msgsState = 0); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message
void feedWereRead(const QVector<MTPint> &msgsIds);
void feedInboxRead(const PeerId &peer, int32 upTo);
void feedOutboxRead(const PeerId &peer, int32 upTo);
void feedWereDeleted(const QVector<MTPint> &msgsIds);
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links);
void feedUserLink(MTPint userId, const MTPcontacts_MyLink &myLink, const MTPcontacts_ForeignLink &foreignLink);
void feedMessageMedia(MsgId msgId, const MTPMessage &msg);
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
int32 maxMsgId();
ImagePtr image(const MTPPhotoSize &size);
@@ -92,6 +96,9 @@ namespace App {
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = 0);
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert = 0);
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert = 0);
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert = 0);
WebPageData *feedWebPage(const MTPWebPage &webpage);
UserData *userLoaded(const PeerId &user);
ChatData *chatLoaded(const PeerId &chat);
@@ -111,13 +118,14 @@ namespace App {
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 = -2);
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
void forgetMedia();
MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo);
Histories &histories();
History *history(const PeerId &peer, int32 unreadCnt = 0);
History *history(const PeerId &peer, int32 unreadCnt = 0, int32 maxInboxRead = 0);
History *historyLoaded(const PeerId &peer);
HistoryItem *histItemById(MsgId itemId);
HistoryItem *historyRegItem(HistoryItem *item);
@@ -125,6 +133,8 @@ namespace App {
void historyUnregItem(HistoryItem *item);
void historyClearMsgs();
void historyClearItems();
void historyRegReply(HistoryReply *reply, HistoryItem *to);
void historyUnregReply(HistoryReply *reply, HistoryItem *to);
// void deleteHistory(const PeerId &peer);
void historyRegRandom(uint64 randomId, MsgId itemId);
@@ -175,11 +185,15 @@ namespace App {
void unregDocumentItem(DocumentData *data, HistoryItem *item);
const DocumentItems &documentItems();
void regWebPageItem(WebPageData *data, HistoryItem *item);
void unregWebPageItem(WebPageData *data, HistoryItem *item);
const WebPageItems &webPageItems();
void setProxySettings(QNetworkAccessManager &manager);
void setProxySettings(QTcpSocket &socket);
void searchByHashtag(const QString &tag);
void openUserByName(const QString &username);
void openUserByName(const QString &username, bool toProfile = false);
void openLocalUrl(const QString &url);
void initBackground(int32 id = 0, const QImage &p = QImage(), bool nowrite = false);

View File

@@ -46,24 +46,16 @@ namespace {
}
}
class _DebugWaiter : public QObject {
class EventFilterForMac : public QObject {
public:
_DebugWaiter(QObject *parent) : QObject(parent), _debugState(0) {
EventFilterForMac(QObject *parent) : QObject(parent) {
}
bool eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
switch (_debugState) {
case 0: if (ev->key() == Qt::Key_F12) _debugState = 1; break;
case 1: if (ev->key() == Qt::Key_F11) _debugState = 2; else if (ev->key() != Qt::Key_F12) _debugState = 0; break;
case 2: if (ev->key() == Qt::Key_F10) _debugState = 3; else if (ev->key() != Qt::Key_F11) _debugState = 0; break;
case 3: if (ev->key() == Qt::Key_F11) _debugState = 4; else if (ev->key() != Qt::Key_F10) _debugState = 0; break;
case 4: if (ev->key() == Qt::Key_F12) offerDebug(); if (ev->key() != Qt::Key_F11) _debugState = 0; break;
}
if (cPlatform() == dbipMac && ev->key() == Qt::Key_W && (ev->modifiers() & (Qt::MetaModifier | Qt::ControlModifier))) {
if (ev->key() == Qt::Key_W && (ev->modifiers() & (Qt::MetaModifier | Qt::ControlModifier))) {
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
App::wnd()->minimizeToTray();
return true;
@@ -72,15 +64,7 @@ namespace {
}
return QObject::eventFilter(o, e);
}
void offerDebug() {
ConfirmBox *box = new ConfirmBox(lang(lng_sure_enable_debug));
connect(box, SIGNAL(confirmed()), App::app(), SLOT(onEnableDebugMode()));
App::wnd()->showLayer(box);
}
private:
int _debugState;
};
}
@@ -101,7 +85,9 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
}
mainApp = this;
installEventFilter(new _DebugWaiter(this));
if (cPlatform() == dbipMac) {
installEventFilter(new EventFilterForMac(this));
}
QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Regular.ttf"));
QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Bold.ttf"));
@@ -306,9 +292,9 @@ void Application::selfPhotoCleared(const MTPUserProfilePhoto &result) {
emit peerPhotoDone(App::self()->id);
}
void Application::chatPhotoCleared(PeerId peer, const MTPmessages_StatedMessage &result) {
void Application::chatPhotoCleared(PeerId peer, const MTPUpdates &updates) {
if (App::main()) {
App::main()->sentFullDataReceived(0, result);
App::main()->sentUpdatesReceived(updates);
}
cancelPhotoUpdate(peer);
emit peerPhotoDone(peer);
@@ -323,16 +309,18 @@ void Application::selfPhotoDone(const MTPphotos_Photo &result) {
emit peerPhotoDone(App::self()->id);
}
void Application::chatPhotoDone(PeerId peer, const MTPmessages_StatedMessage &result) {
void Application::chatPhotoDone(PeerId peer, const MTPUpdates &updates) {
if (App::main()) {
App::main()->sentFullDataReceived(0, result);
App::main()->sentUpdatesReceived(updates);
}
cancelPhotoUpdate(peer);
emit peerPhotoDone(peer);
}
bool Application::peerPhotoFail(PeerId peer, const RPCError &e) {
LOG(("Application Error: update photo failed %1: %2").arg(e.type()).arg(e.description()));
bool Application::peerPhotoFail(PeerId peer, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("Application Error: update photo failed %1: %2").arg(error.type()).arg(error.description()));
cancelPhotoUpdate(peer);
emit peerPhotoFail(peer);
return true;
@@ -346,12 +334,6 @@ void Application::peerClearPhoto(PeerId peer) {
}
}
//void Application::writeUserConfigIn(uint64 ms) {
// if (!writeUserConfigTimer.isActive()) {
// writeUserConfigTimer.start(ms);
// }
//}
void Application::killDownloadSessionsStart(int32 dc) {
if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) {
killDownloadSessionTimes.insert(dc, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout);
@@ -372,10 +354,6 @@ void Application::checkLocalTime() {
if (App::main()) App::main()->checkLastUpdate(checkms());
}
//void Application::onWriteUserConfig() {
// Local::writeUserSettings();
//}
void Application::onAppStateChanged(Qt::ApplicationState state) {
checkLocalTime();
if (window) window->updateIsActive((state == Qt::ApplicationActive) ? cOnlineFocusTimeout() : cOfflineBlurTimeout());
@@ -416,8 +394,14 @@ void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) {
}
}
void Application::onEnableDebugMode() {
if (!cDebug()) {
void Application::onSwitchDebugMode() {
if (cDebug()) {
QFile(cWorkingDir() + qsl("tdata/withdebug")).remove();
cSetDebug(false);
cSetRestarting(true);
cSetRestartingToSettings(true);
App::quit();
} else {
logsInitDebug();
cSetDebug(true);
QFile f(cWorkingDir() + qsl("tdata/withdebug"));
@@ -425,8 +409,25 @@ void Application::onEnableDebugMode() {
f.write("1");
f.close();
}
App::wnd()->hideLayer();
}
App::wnd()->hideLayer();
}
void Application::onSwitchTestMode() {
if (cTestMode()) {
QFile(cWorkingDir() + qsl("tdata/withtestmode")).remove();
cSetTestMode(false);
} else {
QFile f(cWorkingDir() + qsl("tdata/withtestmode"));
if (f.open(QIODevice::WriteOnly)) {
f.write("1");
f.close();
}
cSetTestMode(true);
}
cSetRestarting(true);
cSetRestartingToSettings(true);
App::quit();
}
Application::UpdatingState Application::updatingState() {
@@ -478,7 +479,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
int32 filesize = 0;
QByteArray data;
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false);
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
@@ -653,10 +654,10 @@ void Application::checkMapVersion() {
psRegisterCustomScheme();
if (Local::oldMapVersion()) {
QString versionFeatures;
if (DevChannel && Local::oldMapVersion() < 7019) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Passcode lock option added");
} else if (!DevChannel && Local::oldMapVersion() < 7020) {
versionFeatures = lang(lng_new_version7020).trimmed();
if (DevChannel && Local::oldMapVersion() < 8002) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Link previews bugfixes\n\xe2\x80\x94 Links in preview descriptions are now clickable\n\xe2\x80\x94 Twitter and Instagram mentions and hashtags in previews are clickable\n\xe2\x80\x94 Fixed file uploading\n\xe2\x80\x94 Fixed photo, document and sticker forwarding").replace('@', qsl("@") + QChar(0x200D));
} else if (!DevChannel && Local::oldMapVersion() < 8003) {
versionFeatures = lang(lng_new_version_text).trimmed();
}
if (!versionFeatures.isEmpty()) {
versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog"));
@@ -671,30 +672,28 @@ void Application::startApp() {
DEBUG_LOG(("Application Info: starting app.."));
Local::ReadMapState state = Local::readMap(QByteArray());
if (state == Local::ReadMapPassNeeded) {
cSetHasPasscode(true);
}
DEBUG_LOG(("Application Info: local map read.."));
window->createWinId();
window->init();
DEBUG_LOG(("Application Info: window created.."));
if (state != Local::ReadMapPassNeeded) {
initImageLinkManager();
App::initMedia();
Local::ReadMapState state = Local::readMap(QByteArray());
if (state == Local::ReadMapPassNeeded) {
cSetHasPasscode(true);
DEBUG_LOG(("Application Info: passcode nneded.."));
} else {
DEBUG_LOG(("Application Info: local map read.."));
MTP::start();
}
MTP::setStateChangedHandler(mtpStateChanged);
MTP::setSessionResetHandler(mtpSessionReset);
DEBUG_LOG(("Application Info: MTP started.."));
initImageLinkManager();
App::initMedia();
DEBUG_LOG(("Application Info: showing."));
if (state == Local::ReadMapPassNeeded) {
window->setupPasscode(false);

View File

@@ -64,9 +64,9 @@ public:
void stopUpdate();
void selfPhotoCleared(const MTPUserProfilePhoto &result);
void chatPhotoCleared(PeerId peer, const MTPmessages_StatedMessage &result);
void chatPhotoCleared(PeerId peer, const MTPUpdates &updates);
void selfPhotoDone(const MTPphotos_Photo &result);
void chatPhotoDone(PeerId peerId, const MTPmessages_StatedMessage &rersult);
void chatPhotoDone(PeerId peerId, const MTPUpdates &updates);
bool peerPhotoFail(PeerId peerId, const RPCError &e);
void peerClearPhoto(PeerId peer);
@@ -107,8 +107,8 @@ public slots:
void photoUpdated(MsgId msgId, const MTPInputFile &file);
void onEnableDebugMode();
// void onWriteUserConfig();
void onSwitchDebugMode();
void onSwitchTestMode();
void killDownloadSessions();
void onAppStateChanged(Qt::ApplicationState state);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -25,11 +25,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
AboutBox::AboutBox() :
_done(this, lang(lng_about_done), st::aboutCloseButton),
_version(this, qsl("[a href=\"https://desktop.telegram.org/#changelog\"]") + textClean(lng_about_version(lt_version, QString::fromWCharArray(AppVersionStr) + (DevChannel ? " dev" : ""))) + qsl("[/a]"), st::aboutVersion, st::defaultTextStyle),
_text(this, lang(lng_about_text), st::aboutLabel, st::aboutTextStyle),
_hiding(false), a_opacity(0, 1) {
_text(this, lang(lng_about_text), st::aboutLabel, st::aboutTextStyle) {
_width = st::aboutWidth;
_height = st::aboutHeight;
resizeMaxHeight(st::aboutWidth, st::aboutHeight);
_version.move(0, st::aboutVersionTop);
_text.move(0, st::aboutTextTop);
@@ -37,15 +35,11 @@ _hiding(false), a_opacity(0, 1) {
_headerWidth = st::aboutHeaderFont->m.width(qsl("Telegram "));
_subheaderWidth = st::aboutSubheaderFont->m.width(qsl("Desktop"));
_done.move(0, _height - _done.height());
_done.move(0, height() - _done.height());
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void AboutBox::hideAll() {
@@ -63,65 +57,21 @@ void AboutBox::showAll() {
void AboutBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
onClose();
} else if (e->key() == Qt::Key_Escape) {
onClose();
} else {
AbstractBox::keyPressEvent(e);
}
}
void AboutBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void AboutBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
if (paint(p)) return;
p.drawPixmap(QPoint((_width - st::aboutIcon.pxWidth()) / 2, st::aboutIconTop), App::sprite(), st::aboutIcon);
p.drawPixmap(QPoint((width() - st::aboutIcon.pxWidth()) / 2, st::aboutIconTop), App::sprite(), st::aboutIcon);
p.setPen(st::black->p);
p.setFont(st::aboutHeaderFont->f);
p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2, st::aboutHeaderTop + st::aboutHeaderFont->ascent, qsl("Telegram"));
p.setPen(st::black->p);
p.setFont(st::aboutHeaderFont->f);
p.drawText((width() - (_headerWidth + _subheaderWidth)) / 2, st::aboutHeaderTop + st::aboutHeaderFont->ascent, qsl("Telegram"));
p.setFont(st::aboutSubheaderFont->f);
p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2 + _headerWidth, st::aboutHeaderTop + st::aboutSubheaderFont->ascent, qsl("Desktop"));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void AboutBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
setFocus();
}
} else {
a_opacity.update(ms, anim::linear);
}
update();
}
void AboutBox::onClose() {
emit closed();
}
void AboutBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
AboutBox::~AboutBox() {
p.setFont(st::aboutSubheaderFont->f);
p.drawText((width() - (_headerWidth + _subheaderWidth)) / 2 + _headerWidth, st::aboutHeaderTop + st::aboutSubheaderFont->ascent, qsl("Desktop"));
}

View File

@@ -17,37 +17,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class AboutBox : public LayeredWidget {
class AboutBox : public AbstractBox {
Q_OBJECT
public:
AboutBox();
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~AboutBox();
public slots:
void onClose();
private:
protected:
void hideAll();
void showAll();
int32 _width, _height;
private:
BottomButton _done;
FlatLabel _version, _text;
int32 _headerWidth, _subheaderWidth;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};

View File

@@ -0,0 +1,159 @@
/*
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 "localstorage.h"
#include "abstractbox.h"
#include "mainwidget.h"
#include "window.h"
AbstractBox::AbstractBox() : _maxHeight(0), _hiding(false), a_opacity(0, 1) {
resize(st::boxWidth, 0);
}
void AbstractBox::prepare() {
showAll();
_cache = myGrab(this, rect());
hideAll();
}
void AbstractBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
onClose();
} else {
LayeredWidget::keyPressEvent(e);
}
}
void AbstractBox::parentResized() {
int32 newHeight = countHeight();
setGeometry((App::wnd()->width() - width()) / 2, (App::wnd()->height() - newHeight) / 2, width(), newHeight);
update();
}
bool AbstractBox::paint(QPainter &p) {
bool result = true;
if (_cache.isNull()) {
result = (_hiding && a_opacity.current() < 0.01);
// fill bg
p.fillRect(rect(), st::boxBG->b);
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
return result;
}
void AbstractBox::paintTitle(Painter &p, const QString &title, bool withShadow) {
if (withShadow) {
// paint shadow
p.fillRect(0, st::boxTitleHeight, width(), st::scrollDef.topsh, st::scrollDef.shColor->b);
}
// 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);
}
void AbstractBox::paintGrayTitle(QPainter &p, const QString &title) {
// draw box title
p.setFont(st::boxFont->f);
p.setPen(st::boxGrayTitle->p);
p.drawText(QRect(st::boxTitlePos.x(), st::boxTitlePos.y(), width() - 2 * st::boxTitlePos.x(), st::boxFont->height), title, style::al_top);
}
void AbstractBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (paint(p)) return;
}
void AbstractBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
showDone();
}
} else {
a_opacity.update(ms, anim::linear);
}
update();
}
void AbstractBox::setMaxHeight(int32 maxHeight) {
resizeMaxHeight(width(), maxHeight);
}
void AbstractBox::resizeMaxHeight(int32 newWidth, int32 maxHeight) {
if (width() != newWidth || _maxHeight != maxHeight) {
_maxHeight = maxHeight;
resize(newWidth, countHeight());
}
}
int32 AbstractBox::countHeight() const {
return qMin(_maxHeight, App::wnd()->height() - int32(2 * st::boxVerticalMargin));
}
void AbstractBox::onClose() {
closePressed();
emit closed();
}
void AbstractBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
ScrollableBox::ScrollableBox(const style::flatScroll &scroll) : AbstractBox(),
_scroll(this, scroll), _innerPtr(0), _topSkip(st::boxTitleHeight), _bottomSkip(0) {
}
void ScrollableBox::resizeEvent(QResizeEvent *e) {
_scroll.setGeometry(0, _topSkip, width(), height() - _topSkip - _bottomSkip);
}
void ScrollableBox::init(QWidget *inner, int32 bottomSkip, int32 topSkip) {
_bottomSkip = bottomSkip;
_topSkip = topSkip;
_innerPtr = inner;
_scroll.setWidget(_innerPtr);
_scroll.setFocusPolicy(Qt::NoFocus);
ScrollableBox::resizeEvent(0);
}
void ScrollableBox::hideAll() {
_scroll.hide();
}
void ScrollableBox::showAll() {
_scroll.show();
}
ItemListBox::ItemListBox(const style::flatScroll &scroll) : ScrollableBox(scroll) {
setMaxHeight(st::boxMaxListHeight);
}

View File

@@ -0,0 +1,95 @@
/*
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 "layerwidget.h"
class AbstractBox : public LayeredWidget {
Q_OBJECT
public:
AbstractBox();
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
public slots:
void onClose();
protected:
void prepare();
bool paint(QPainter &p);
void paintTitle(Painter &p, const QString &title, bool withShadow);
void paintGrayTitle(QPainter &p, const QString &title);
void setMaxHeight(int32 maxHeight);
void resizeMaxHeight(int32 newWidth, int32 maxHeight);
virtual void closePressed() {
}
virtual void hideAll() {
}
virtual void showAll() {
}
virtual void showDone() {
setFocus();
}
private:
int32 _maxHeight;
int32 countHeight() const;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};
class ScrollableBox : public AbstractBox {
public:
ScrollableBox(const style::flatScroll &scroll);
void resizeEvent(QResizeEvent *e);
protected:
void init(QWidget *inner, int32 bottomSkip = 0, int32 topSkip = st::boxTitleHeight);
virtual void hideAll();
virtual void showAll();
ScrollArea _scroll;
private:
QWidget *_innerPtr;
int32 _topSkip, _bottomSkip;
};
class ItemListBox : public ScrollableBox {
public:
ItemListBox(const style::flatScroll &scroll);
};

View File

@@ -31,7 +31,7 @@ AddContactBox::AddContactBox(QString fname, QString lname, QString phone) :
_firstInput(this, st::inpAddContact, lang(lng_signup_firstname), fname),
_lastInput(this, st::inpAddContact, lang(lng_signup_lastname), lname),
_phoneInput(this, st::inpAddContact, lang(lng_contact_phone), phone.isEmpty() ? phone : App::formatPhone(phone)),
_contactId(0), _addRequest(0), a_opacity(0, 1), _hiding(false) {
_contactId(0), _addRequest(0) {
if (!phone.isEmpty()) {
_phoneInput.setDisabled(true);
@@ -48,45 +48,32 @@ AddContactBox::AddContactBox(PeerData *peer) :
_firstInput(this, st::inpAddContact, lang(peer->chat ? lng_dlg_new_group_name : lng_signup_firstname), peer->chat ? peer->name : peer->asUser()->firstName),
_lastInput(this, st::inpAddContact, lang(lng_signup_lastname), peer->chat ? QString() : peer->asUser()->lastName),
_phoneInput(this, st::inpAddContact, lang(lng_contact_phone)),
_contactId(0), _addRequest(0), a_opacity(0, 1), _hiding(false) {
_contactId(0), _addRequest(0) {
initBox();
}
void AddContactBox::initBox() {
_width = st::addContactWidth;
if (_peer) {
if (_peer->chat) {
_boxTitle = lang(lng_edit_group_title);
_height = st::addContactTitleHeight + st::addContactPadding.top() + 1 * _firstInput.height() + st::addContactPadding.bottom() + _addButton.height();
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 1 * _firstInput.height() + st::addContactPadding.bottom() + _addButton.height());
} else {
_boxTitle = lang(_peer == App::self() ? lng_edit_self_title : lng_edit_contact_title);
_height = st::addContactTitleHeight + st::addContactPadding.top() + 2 * _firstInput.height() + 1 * st::addContactDelta + st::addContactPadding.bottom() + _addButton.height();
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 2 * _firstInput.height() + 1 * st::addContactDelta + st::addContactPadding.bottom() + _addButton.height());
}
} else {
bool readyToAdd = !_phoneInput.text().isEmpty() && (!_firstInput.text().isEmpty() || !_lastInput.text().isEmpty());
_boxTitle = lang(readyToAdd ? lng_confirm_contact_data : lng_enter_contact_data);
_height = st::addContactTitleHeight + st::addContactPadding.top() + 3 * _firstInput.height() + 2 * st::addContactDelta + st::addContactPadding.bottom() + _addButton.height();
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 3 * _firstInput.height() + 2 * st::addContactDelta + st::addContactPadding.bottom() + _addButton.height());
}
_firstInput.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _firstInput.height());
_lastInput.setGeometry(st::addContactPadding.left(), _firstInput.y() + _firstInput.height() + st::addContactDelta, _firstInput.width(), _firstInput.height());
_phoneInput.setGeometry(st::addContactPadding.left(), _lastInput.y() + _lastInput.height() + st::addContactDelta, _lastInput.width(), _lastInput.height());
int32 buttonTop = (_peer ? (_peer->chat ? _firstInput : _lastInput) : _phoneInput).y() + _phoneInput.height() + st::addContactPadding.bottom();
_cancelButton.move(0, buttonTop);
_addButton.move(_width - _addButton.width(), buttonTop);
_retryButton.move(_width - _retryButton.width(), buttonTop);
_retryButton.hide();
connect(&_addButton, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_retryButton, SIGNAL(clicked()), this, SLOT(onRetry()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void AddContactBox::hideAll() {
@@ -114,6 +101,14 @@ void AddContactBox::showAll() {
_cancelButton.show();
}
void AddContactBox::showDone() {
if ((_firstInput.text().isEmpty() && _lastInput.text().isEmpty()) || _phoneInput.isHidden() || !_phoneInput.isEnabled()) {
_firstInput.setFocus();
} else {
_phoneInput.setFocus();
}
}
void AddContactBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_firstInput.hasFocus()) {
@@ -154,65 +149,40 @@ void AddContactBox::keyPressEvent(QKeyEvent *e) {
onSend();
}
}
} else if (e->key() == Qt::Key_Escape) {
onCancel();
} else {
AbstractBox::keyPressEvent(e);
}
}
void AddContactBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void AddContactBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
Painter p(this);
if (paint(p)) return;
// paint shadows
if (_retryButton.isHidden()) {
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
}
p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
if (_retryButton.isHidden()) {
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, _boxTitle);
} else {
int32 h = size().height() - st::boxPadding.top() * 2 - _retryButton.height() - st::boxPadding.bottom();
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), _width - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft);
}
}
if (_retryButton.isHidden()) {
paintTitle(p, _boxTitle, true);
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
// draw box text
p.setPen(st::black->p);
p.setFont(st::boxTitleFont->f);
int32 h = size().height() - st::boxPadding.top() * 2 - _retryButton.height() - st::boxPadding.bottom();
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), width() - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft);
}
// paint shadows
p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
void AddContactBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
if ((_firstInput.text().isEmpty() && _lastInput.text().isEmpty()) || _phoneInput.isHidden() || !_phoneInput.isEnabled()) {
_firstInput.setFocus();
} else {
_phoneInput.setFocus();
}
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
void AddContactBox::resizeEvent(QResizeEvent *e) {
_firstInput.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _firstInput.height());
_lastInput.setGeometry(st::addContactPadding.left(), _firstInput.y() + _firstInput.height() + st::addContactDelta, _firstInput.width(), _firstInput.height());
_phoneInput.setGeometry(st::addContactPadding.left(), _lastInput.y() + _lastInput.height() + st::addContactDelta, _lastInput.width(), _lastInput.height());
_cancelButton.move(0, height() - _cancelButton.height());
_addButton.move(width() - _addButton.width(), height() - _addButton.height());
_retryButton.move(width() - _retryButton.width(), height() - _retryButton.height());
}
void AddContactBox::onSend() {
@@ -256,7 +226,8 @@ void AddContactBox::onSaveSelfDone(const MTPUser &user) {
}
bool AddContactBox::onSaveSelfFail(const RPCError &error) {
_addRequest = 0;
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
QString err(error.type());
QString firstName = textOneLine(_firstInput.text()), lastName = textOneLine(_lastInput.text());
if (err == "NAME_NOT_MODIFIED") {
@@ -277,6 +248,8 @@ bool AddContactBox::onSaveSelfFail(const RPCError &error) {
}
bool AddContactBox::onSaveFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_addRequest = 0;
QString err(error.type());
QString firstName = _firstInput.text().trimmed(), lastName = _lastInput.text().trimmed();
@@ -294,7 +267,7 @@ bool AddContactBox::onSaveFail(const RPCError &error) {
}
void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
if (_hiding || !App::main()) return;
if (isHidden() || !App::main()) return;
const MTPDcontacts_importedContacts &d(res.c_contacts_importedContacts());
App::feedUsers(d.vusers);
@@ -320,17 +293,15 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
_lastInput.hide();
_phoneInput.hide();
_retryButton.show();
int32 theight = st::addContactTitleFont->m.boundingRect(0, 0, _width - st::boxPadding.left() - st::boxPadding.right(), 1, Qt::TextWordWrap, lng_contact_not_joined(lt_name, _sentName)).height();
int32 theight = st::boxTitleFont->m.boundingRect(0, 0, width() - st::boxPadding.left() - st::boxPadding.right(), 1, Qt::TextWordWrap, lng_contact_not_joined(lt_name, _sentName)).height();
int32 h = st::boxPadding.top() * 2 + theight + _retryButton.height() + st::boxPadding.bottom();
resize(_width, h);
_retryButton.move(_retryButton.x(), h - _retryButton.height());
_cancelButton.move(_cancelButton.x(), h - _retryButton.height());
setMaxHeight(h);
update();
}
}
void AddContactBox::onSaveChatDone(const MTPmessages_StatedMessage &result) {
App::main()->sentFullDataReceived(0, result);
void AddContactBox::onSaveChatDone(const MTPUpdates &updates) {
App::main()->sentUpdatesReceived(updates);
emit closed();
}
@@ -340,10 +311,6 @@ void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
emit closed();
}
void AddContactBox::onCancel() {
emit closed();
}
void AddContactBox::onRetry() {
_addRequest = 0;
_contactId = 0;
@@ -359,18 +326,6 @@ void AddContactBox::onRetry() {
_phoneInput.setDisabled(false);
_retryButton.hide();
_firstInput.setFocus();
resize(_width, _height);
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 3 * _firstInput.height() + 2 * st::addContactDelta + st::addContactPadding.bottom() + _addButton.height());
update();
}
void AddContactBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
AddContactBox::~AddContactBox() {
}

View File

@@ -17,39 +17,38 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class AddContactBox : public LayeredWidget, public RPCSender {
class AddContactBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
AddContactBox(QString fname = QString(), QString lname = QString(), QString phone = QString());
AddContactBox(PeerData *peer);
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~AddContactBox();
void resizeEvent(QResizeEvent *e);
public slots:
void onSend();
void onRetry();
void onCancel();
private:
protected:
void hideAll();
void showAll();
void showDone();
private:
void onImportDone(const MTPcontacts_ImportedContacts &res);
void onSaveSelfDone(const MTPUser &user);
bool onSaveSelfFail(const RPCError &error);
void onSaveChatDone(const MTPmessages_StatedMessage &result);
void onSaveChatDone(const MTPUpdates &updates);
void onSaveUserDone(const MTPcontacts_ImportedContacts &res);
bool onSaveFail(const RPCError &e);
@@ -58,17 +57,11 @@ private:
PeerData *_peer;
QString _boxTitle;
int32 _width, _height;
FlatButton _addButton, _retryButton, _cancelButton;
FlatInput _firstInput, _lastInput, _phoneInput;
uint64 _contactId;
QPixmap _cache;
mtpRequestId _addRequest;
QString _sentName;
anim::fvalue a_opacity;
bool _hiding;
};

View File

@@ -1,685 +0,0 @@
/*
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 "addparticipantbox.h"
#include "mainwidget.h"
#include "window.h"
AddParticipantInner::AddParticipantInner(ChatData *chat) : _chat(chat),
_contacts(&App::main()->contactsList()),
_sel(0),
_filteredSel(-1),
_mouseSel(false),
_selCount(0),
_addContactLnk(this, lang(lng_add_contact_button)) {
connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) {
r->attached = 0;
}
_filter = qsl("a");
updateFilter();
connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow*,DialogRow*)), this, SLOT(onDialogRowReplaced(DialogRow*,DialogRow*)));
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
}
void AddParticipantInner::peerUpdated(PeerData *peer) {
if (!peer || peer == _chat) {
if (_chat->forbidden) {
App::wnd()->hideLayer();
} else if (!_chat->participants.isEmpty() || _chat->count <= 0) {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i ) {
delete i.value();
}
_contactsData.clear();
for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
row->attached = 0;
}
if (!_filter.isEmpty()) {
for (int32 j = 0, s = _filtered.size(); j < s; ++j) {
_filtered[j]->attached = 0;
}
}
}
} else if (!peer->chat) {
ContactsData::iterator i = _contactsData.find(peer->asUser());
if (i != _contactsData.cend()) {
for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
if (row->attached == i.value()) row->attached = 0;
}
if (!_filter.isEmpty()) {
for (int32 j = 0, s = _filtered.size(); j < s; ++j) {
if (_filtered[j]->attached == i.value()) _filtered[j]->attached = 0;
}
}
delete i.value();
_contactsData.erase(i);
}
}
parentWidget()->update();
}
void AddParticipantInner::loadProfilePhotos(int32 yFrom) {
int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5;
MTP::clearLoaderPriorities();
if (yTo < 0) return;
if (yFrom < 0) yFrom = 0;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
_contacts->list.adjustCurrent(yFrom, rh);
for (
DialogRow *preloadFrom = _contacts->list.current;
preloadFrom != _contacts->list.end && preloadFrom->pos * rh < yTo;
preloadFrom = preloadFrom->next
) {
preloadFrom->history->peer->photo->load();
}
}
} else if (!_filtered.isEmpty()) {
int32 from = yFrom / rh;
if (from < 0) from = 0;
if (from < _filtered.size()) {
int32 to = (yTo / rh) + 1;
if (to > _filtered.size()) to = _filtered.size();
for (; from < to; ++from) {
_filtered[from]->history->peer->photo->load();
}
}
}
}
AddParticipantInner::ContactData *AddParticipantInner::contactData(DialogRow *row) {
ContactData *data = (ContactData*)row->attached;
if (!data) {
UserData *user = row->history->peer->asUser();
ContactsData::const_iterator i = _contactsData.constFind(user);
if (i == _contactsData.cend()) {
_contactsData.insert(user, data = new ContactData());
data->inchat = _chat->participants.constFind(user) != _chat->participants.cend();
data->check = false;
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
data->online = App::onlineText(user, _time);
} else {
data = i.value();
}
row->attached = data;
}
return data;
}
void AddParticipantInner::paintDialog(QPainter &p, DialogRow *row, bool sel) {
int32 left = st::profileListPadding.width();
UserData *user = row->history->peer->asUser();
ContactData *data = contactData(row);
if (data->inchat || data->check || _selCount + _chat->count >= cMaxGroupCount()) {
sel = false;
}
if (sel || data->inchat || data->check) {
p.fillRect(0, 0, width(), 2 * st::profileListPadding.height() + st::profileListPhotoSize, ((data->inchat || data->check) ? st::profileActiveBG : st::profileHoverBG)->b);
}
p.drawPixmap(left, st::profileListPadding.height(), user->photo->pix(st::profileListPhotoSize));
if (data->inchat || data->check) {
p.setPen(st::white->p);
} else {
p.setPen(st::profileListNameColor->p);
}
data->name.drawElided(p, left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListNameTop, width() - st::participantDelta - st::profileListPadding.width() * 2 - st::profileListPhotoSize - st::profileListPadding.width() * 2);
if (sel || data->check) {
p.drawPixmap(QPoint(width() - st::profileCheckRect.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::profileCheckRect.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), (data->check ? st::profileCheckActiveRect : st::profileCheckRect));
}
p.setFont(st::profileSubFont->f);
if (data->inchat || data->check) {
p.setPen(st::white->p);
} else {
p.setPen((App::onlineColorUse(user->onlineTill, _time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
}
p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online);
}
void AddParticipantInner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
QPainter p(this);
_time = unixtime();
p.fillRect(r, st::white->b);
int32 yFrom = r.top();
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
_contacts->list.adjustCurrent(yFrom, rh);
DialogRow *drawFrom = _contacts->list.current;
p.translate(0, drawFrom->pos * rh);
while (drawFrom != _contacts->list.end && drawFrom->pos * rh < r.bottom()) {
paintDialog(p, drawFrom, (drawFrom == _sel));
p.translate(0, rh);
drawFrom = drawFrom->next;
}
} else {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
}
} else {
if (_filtered.isEmpty()) {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_not_found), style::al_center);
} else {
int32 from = yFrom / rh;
if (from < 0) from = 0;
if (from < _filtered.size()) {
int32 to = (r.bottom() / rh) + 1;
if (to > _filtered.size()) to = _filtered.size();
p.translate(0, from * rh);
for (; from < to; ++from) {
paintDialog(p, _filtered[from], (_filteredSel == from));
p.translate(0, rh);
}
}
}
}
}
void AddParticipantInner::enterEvent(QEvent *e) {
setMouseTracking(true);
}
void AddParticipantInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
updateSel();
}
void AddParticipantInner::mouseMoveEvent(QMouseEvent *e) {
_mouseSel = true;
_lastMousePos = e->globalPos();
updateSel();
}
void AddParticipantInner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true;
_lastMousePos = e->globalPos();
updateSel();
if (e->button() == Qt::LeftButton) {
chooseParticipant();
}
}
void AddParticipantInner::chooseParticipant() {
_time = unixtime();
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
if (_filter.isEmpty()) {
if (!_sel || contactData(_sel)->inchat) return;
changeCheckState(_sel);
} else {
if (_filteredSel < 0 || _filteredSel >= _filtered.size() || contactData(_filtered[_filteredSel])->inchat) return;
DialogRow *row = _filtered[_filteredSel];
changeCheckState(row);
PeerData *peer = row->history->peer;
emit selectAllQuery();
}
parentWidget()->update();
}
void AddParticipantInner::changeCheckState(DialogRow *row) {
if (contactData(row)->check) {
contactData(row)->check = false;
--_selCount;
} else if (_selCount + _chat->count < cMaxGroupCount()) {
contactData(row)->check = true;
++_selCount;
}
}
ChatData *AddParticipantInner::chat() {
return _chat;
}
QVector<UserData*> AddParticipantInner::selected() {
QVector<UserData*> result;
result.reserve(_contactsData.size());
for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) {
if (i.value()->check) {
result.push_back(i.key());
}
}
return result;
}
void AddParticipantInner::updateSel() {
if (!_mouseSel) return;
QPoint p(mapFromGlobal(_lastMousePos));
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
DialogRow *newSel = rect().contains(p) ? _contacts->list.rowAtY(p.y(), rh) : 0;
if (newSel != _sel) {
_sel = newSel;
parentWidget()->update();
}
} else {
int32 newFilteredSel = (p.y() >= 0 && rect().contains(p)) ? (p.y() / rh) : -1;
if (newFilteredSel != _filteredSel) {
_filteredSel = newFilteredSel;
parentWidget()->update();
}
}
}
void AddParticipantInner::updateFilter(QString filter) {
filter = textSearchKey(filter);
_time = unixtime();
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) {
int32 rh = (st::profileListPhotoSize + st::profileListPadding.height() * 2);
_filter = filter;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
resize(width(), _contacts->list.count * rh);
_sel = _contacts->list.begin;
while (_sel->next->next &&& contactData(_sel)->inchat) {
_sel = _sel->next;
}
} else {
resize(width(), st::noContactsHeight);
if (cContactsReceived()) {
if (_addContactLnk.isHidden()) _addContactLnk.show();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
}
}
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
_filtered.clear();
if (!f.isEmpty()) {
DialogsList *dialogsToFilter = 0;
if (_contacts->list.count) {
for (fi = fb; fi != fe; ++fi) {
DialogsIndexed::DialogsIndex::iterator i = _contacts->index.find(fi->at(0));
if (i == _contacts->index.cend()) {
dialogsToFilter = 0;
break;
}
if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) {
dialogsToFilter = i.value();
}
}
}
if (dialogsToFilter && dialogsToFilter->count) {
_filtered.reserve(dialogsToFilter->count);
for (DialogRow *i = dialogsToFilter->begin, *e = dialogsToFilter->end; i != e; i = i->next) {
const PeerData::Names &names(i->history->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (ni == ne) {
break;
}
}
if (fi == fe) {
i->attached = 0;
_filtered.push_back(i);
}
}
}
}
_filteredSel = _filtered.isEmpty() ? -1 : 0;
while (_filteredSel < _filtered.size() - 1 && contactData(_filtered[_filteredSel])->inchat) {
++_filteredSel;
}
if (!_filtered.isEmpty()) {
resize(width(), _filtered.size() * rh);
} else {
resize(width(), st::noContactsHeight);
}
}
if (parentWidget()) parentWidget()->update();
loadProfilePhotos(0);
}
}
void AddParticipantInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
if (!_filter.isEmpty()) {
for (FilteredDialogs::iterator i = _filtered.begin(), e = _filtered.end(); i != e;) {
if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts!
if (newRow) {
*i = newRow;
++i;
} else {
i = _filtered.erase(i);
}
} else {
++i;
}
}
if (_filteredSel >= _filtered.size()) {
_filteredSel = -1;
}
} else {
if (_sel == oldRow) {
_sel = newRow;
}
}
_mouseSel = false;
int32 rh = (st::profileListPhotoSize + st::profileListPadding.height() * 2);
int32 newh = (_filter.isEmpty() ? _contacts->list.count : _filtered.size()) * rh;
resize(width(), newh);
}
AddParticipantInner::~AddParticipantInner() {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
delete *i;
}
}
void AddParticipantInner::resizeEvent(QResizeEvent *e) {
_addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2);
}
void AddParticipantInner::selectSkip(int32 dir) {
_time = unixtime();
_mouseSel = false;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, origDir = dir;
if (_filter.isEmpty()) {
if (_sel) {
if (dir > 0) {
while (dir && _sel->next->next) {
_sel = _sel->next;
--dir;
}
while (contactData(_sel)->inchat && _sel->next->next) {
_sel = _sel->next;
}
if (contactData(_sel)->inchat) {
while (contactData(_sel)->inchat && _sel->prev) {
_sel = _sel->prev;
}
}
} else {
while (dir && _sel->prev) {
_sel = _sel->prev;
++dir;
}
while (contactData(_sel)->inchat && _sel->prev) {
_sel = _sel->prev;
}
if (contactData(_sel)->inchat) {
while (contactData(_sel)->inchat && _sel->next->next) {
_sel = _sel->next;
}
}
}
} else if (dir > 0 && _contacts->list.count) {
_sel = _contacts->list.begin;
while (contactData(_sel)->inchat && _sel->next->next) {
_sel = _sel->next;
}
}
if (_sel) {
if (contactData(_sel)->inchat) {
_sel = 0;
} else {
emit mustScrollTo(_sel->pos * rh, (_sel->pos + 1) * rh);
}
}
} else {
if (dir > 0) {
if (_filteredSel < 0 && dir > 1) {
_filteredSel = 0;
}
_filteredSel += dir;
while (_filteredSel < _filtered.size() - 1 && contactData(_filtered[_filteredSel])->inchat) {
++_filteredSel;
}
if (_filteredSel >= _filtered.size()) {
_filteredSel = _filtered.size() - 1;
}
while (_filteredSel > 0 && contactData(_filtered[_filteredSel])->inchat) {
--_filteredSel;
}
} else if (_filteredSel > 0) {
_filteredSel += dir;
if (_filteredSel < 0) {
_filteredSel = 0;
}
if (_filteredSel < _filtered.size() - 1) {
while (_filteredSel > 0 && contactData(_filtered[_filteredSel])->inchat) {
--_filteredSel;
}
}
while (_filteredSel < _filtered.size() - 1 && contactData(_filtered[_filteredSel])->inchat) {
++_filteredSel;
}
}
if (_filteredSel >= 0) {
if (contactData(_filtered[_filteredSel])->inchat) {
_filteredSel = -1;
} else {
emit mustScrollTo(_filteredSel * rh, (_filteredSel + 1) * rh);
}
}
}
parentWidget()->update();
}
void AddParticipantInner::selectSkipPage(int32 h, int32 dir) {
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
int32 points = h / rh;
if (!points) return;
selectSkip(points * dir);
}
AddParticipantBox::AddParticipantBox(ChatData *chat) :
_scroll(this, st::newGroupScroll), _inner(chat),
_filter(this, st::contactsFilter, lang(lng_participant_filter)),
_invite(this, lang(lng_participant_invite), st::btnSelectDone),
_cancel(this, lang(lng_cancel), st::btnSelectCancel),
_hiding(false), a_opacity(0, 1), af_opacity(anim::linear) {
_width = st::participantWidth;
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
if (_height > st::participantMaxHeight) _height = st::participantMaxHeight;
resize(_width, _height);
_scroll.setWidget(&_inner);
_scroll.setFocusPolicy(Qt::NoFocus);
connect(&_invite, SIGNAL(clicked()), this, SLOT(onInvite()));
connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(closed()));
connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSel()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
connect(&_filter, SIGNAL(cancelled()), this, SLOT(onClose()));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll()));
showAll();
_cache = myGrab(this, rect());
hideAll();
}
void AddParticipantBox::hideAll() {
_filter.hide();
_scroll.hide();
_cancel.hide();
_invite.hide();
}
void AddParticipantBox::showAll() {
_filter.show();
_scroll.show();
_cancel.show();
_invite.show();
}
void AddParticipantBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
onClose();
} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
_inner.chooseParticipant();
} else if (e->key() == Qt::Key_Down) {
_inner.selectSkip(1);
} else if (e->key() == Qt::Key_Up) {
_inner.selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner.selectSkipPage(_scroll.height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner.selectSkipPage(_scroll.height(), -1);
} else {
e->ignore();
}
}
void AddParticipantBox::parentResized() {
QSize s = parentWidget()->size();
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
if (_height > st::participantMaxHeight) _height = st::participantMaxHeight;
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void AddParticipantBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
// paint shadows
p.fillRect(0, st::participantFilter.height, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_profile_add_participant));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void AddParticipantBox::resizeEvent(QResizeEvent *e) {
LayeredWidget::resizeEvent(e);
_filter.move(st::newGroupNamePadding.left(), st::contactsAdd.height + st::newGroupNamePadding.top());
_inner.resize(_width, _inner.height());
_scroll.resize(_width, _height - st::contactsAdd.height - st::newGroupNamePadding.top() - _filter.height() - st::newGroupNamePadding.bottom() - _cancel.height());
_scroll.move(0, _filter.y() + _filter.height() + st::newGroupNamePadding.bottom());
_invite.move(width() - _invite.width(), _height - _invite.height());
_cancel.move(0, _height - _cancel.height());
}
void AddParticipantBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
_filter.setFocus();
}
} else {
a_opacity.update(dt, af_opacity);
}
update();
}
void AddParticipantBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
void AddParticipantBox::onFilterUpdate() {
_scroll.scrollToY(0);
_inner.updateFilter(_filter.text());
}
void AddParticipantBox::onClose() {
emit closed();
}
void AddParticipantBox::onInvite() {
QVector<UserData*> users(_inner.selected());
if (users.isEmpty()) {
_filter.setFocus();
return;
}
App::main()->addParticipants(_inner.chat(), users);
}
void AddParticipantBox::onScroll() {
_inner.loadProfilePhotos(_scroll.scrollTop());
}
AddParticipantBox::~AddParticipantBox() {
}

View File

@@ -1,133 +0,0 @@
/*
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 "layerwidget.h"
class AddParticipantInner : public QWidget, public RPCSender {
Q_OBJECT
public:
AddParticipantInner(ChatData *chat);
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void paintDialog(QPainter &p, DialogRow *row, bool sel);
void updateFilter(QString filter = QString());
void selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir);
void loadProfilePhotos(int32 yFrom);
void chooseParticipant();
void changeCheckState(DialogRow *row);
ChatData *chat();
QVector<UserData*> selected();
~AddParticipantInner();
signals:
void mustScrollTo(int ymin, int ymax);
void selectAllQuery();
public slots:
void onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow);
void updateSel();
void peerUpdated(PeerData *peer);
private:
ChatData *_chat;
int32 _time;
DialogsIndexed *_contacts;
DialogRow *_sel;
QString _filter;
typedef QVector<DialogRow*> FilteredDialogs;
FilteredDialogs _filtered;
int32 _filteredSel;
bool _mouseSel;
int32 _selCount;
typedef struct {
Text name;
QString online;
bool inchat;
bool check;
} ContactData;
typedef QMap<UserData*, ContactData*> ContactsData;
ContactsData _contactsData;
ContactData *contactData(DialogRow *row);
QPoint _lastMousePos;
LinkButton _addContactLnk;
};
class AddParticipantBox : public LayeredWidget {
Q_OBJECT
public:
AddParticipantBox(ChatData *chat);
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void startHide();
~AddParticipantBox();
public slots:
void onFilterUpdate();
void onClose();
void onScroll();
void onInvite();
private:
void hideAll();
void showAll();
ScrollArea _scroll;
AddParticipantInner _inner;
int32 _width, _height;
FlatInput _filter;
FlatButton _invite, _cancel;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
anim::transition af_opacity;
};

View File

@@ -26,17 +26,15 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "window.h"
AutoLockBox::AutoLockBox() :
_done(this, lang(lng_about_done), st::langsCloseButton),
_hiding(false), a_opacity(0, 1) {
_done(this, lang(lng_about_done), st::langsCloseButton) {
bool haveTestLang = (cLang() == languageTest);
int32 opts[] = { 60, 300, 3600, 18000 }, cnt = sizeof(opts) / sizeof(opts[0]);
_width = st::langsWidth;
_height = st::addContactTitleHeight + st::langsPadding.top() + st::langsPadding.bottom() + cnt * (st::langPadding.top() + st::rbDefFlat.height + st::langPadding.bottom()) + _done.height();
resizeMaxHeight(st::langsWidth, st::boxTitleHeight + st::langsPadding.top() + st::langsPadding.bottom() + cnt * (st::langPadding.top() + st::rbDefFlat.height + st::langPadding.bottom()) + _done.height());
int32 y = st::addContactTitleHeight + st::langsPadding.top();
int32 y = st::boxTitleHeight + st::langsPadding.top();
_options.reserve(cnt);
for (int32 i = 0; i < cnt; ++i) {
int32 v = opts[i];
@@ -46,15 +44,10 @@ _hiding(false), a_opacity(0, 1) {
connect(_options.back(), SIGNAL(changed()), this, SLOT(onChange()));
}
_done.move(0, _height - _done.height());
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
_done.move(0, height() - _done.height());
prepare();
}
void AutoLockBox::hideAll() {
@@ -71,51 +64,11 @@ void AutoLockBox::showAll() {
}
}
void AutoLockBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
onClose();
}
}
void AutoLockBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void AutoLockBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
Painter p(this);
if (paint(p)) return;
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
// draw box title / text
p.setFont(st::addContactTitleFont->f);
p.setPen(st::black->p);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_passcode_autolock));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void AutoLockBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
setFocus();
}
} else {
a_opacity.update(ms, anim::linear);
}
update();
paintTitle(p, lang(lng_passcode_autolock), true);
}
void AutoLockBox::onChange() {
@@ -132,19 +85,6 @@ void AutoLockBox::onChange() {
onClose();
}
void AutoLockBox::onClose() {
emit closed();
}
void AutoLockBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
AutoLockBox::~AutoLockBox() {
for (int32 i = 0, l = _options.size(); i < l; ++i) {
delete _options[i];

View File

@@ -17,37 +17,28 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class AutoLockBox : public LayeredWidget {
class AutoLockBox : public AbstractBox {
Q_OBJECT
public:
AutoLockBox();
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~AutoLockBox();
public slots:
void onChange();
void onClose();
private:
protected:
void hideAll();
void showAll();
private:
QVector<FlatRadiobutton*> _options;
int32 _width, _height;
BottomButton _done;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};

View File

@@ -171,100 +171,39 @@ BackgroundInner::~BackgroundInner() {
void BackgroundInner::resizeEvent(QResizeEvent *e) {
}
BackgroundBox::BackgroundBox() : _scroll(this, st::backgroundScroll), _inner(),
_close(this, lang(lng_contacts_done), st::contactsClose),
_hiding(false), a_opacity(0, 1) {
BackgroundBox::BackgroundBox() : ItemListBox(st::boxScroll), _inner(),
_close(this, lang(lng_contacts_done), st::contactsClose) {
_width = st::participantWidth;
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
resize(_width, _height);
resizeEvent(0);
_scroll.setWidget(&_inner);
_scroll.setFocusPolicy(Qt::NoFocus);
init(&_inner, _close.height(), st::boxFont->height + st::newGroupNamePadding.top() + st::newGroupNamePadding.bottom());
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_inner, SIGNAL(backgroundChosen(int)), this, SLOT(onBackgroundChosen(int)));
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void BackgroundBox::hideAll() {
_scroll.hide();
ItemListBox::hideAll();
_close.hide();
}
void BackgroundBox::showAll() {
_scroll.show();
ItemListBox::showAll();
_close.show();
_close.raise();
}
void BackgroundBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
onClose();
} else {
e->ignore();
}
}
void BackgroundBox::parentResized() {
QSize s = parentWidget()->size();
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
if (_height > st::participantMaxHeight) _height = st::participantMaxHeight;
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void BackgroundBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
if (paint(p)) return;
// draw box title / text
p.setFont(st::boxFont->f);
p.setPen(st::boxGrayTitle->p);
p.drawText(QRect(st::addContactTitlePos.x(), st::addContactTitlePos.y(), _width - 2 * st::addContactTitlePos.x(), st::boxFont->height), lang(lng_backgrounds_header), style::al_top);
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
paintGrayTitle(p, lang(lng_backgrounds_header));
}
void BackgroundBox::resizeEvent(QResizeEvent *e) {
LayeredWidget::resizeEvent(e);
_inner.resize(_width, _inner.height());
_scroll.resize(_width, _height - st::boxFont->height - st::newGroupNamePadding.top() - st::newGroupNamePadding.bottom() - _close.height());
_scroll.move(0, st::boxFont->height + st::newGroupNamePadding.top() + st::newGroupNamePadding.bottom());
_close.move(0, _height - _close.height());
}
void BackgroundBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
}
void BackgroundBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
ItemListBox::resizeEvent(e);
_inner.resize(width(), _inner.height());
_close.move(0, height() - _close.height());
}
void BackgroundBox::onBackgroundChosen(int index) {
@@ -275,11 +214,3 @@ void BackgroundBox::onBackgroundChosen(int index) {
}
emit closed();
}
void BackgroundBox::onClose() {
emit closed();
}
BackgroundBox::~BackgroundBox() {
}

View File

@@ -17,7 +17,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class BackgroundInner : public QWidget, public RPCSender {
Q_OBJECT
@@ -48,37 +48,26 @@ private:
};
class BackgroundBox : public LayeredWidget, public RPCSender {
class BackgroundBox : public ItemListBox {
Q_OBJECT
public:
BackgroundBox();
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void startHide();
~BackgroundBox();
public slots:
void onBackgroundChosen(int index);
void onClose();
private:
protected:
void hideAll();
void showAll();
ScrollArea _scroll;
private:
BackgroundInner _inner;
int32 _width, _height;
BottomButton _close;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};

View File

@@ -29,11 +29,11 @@ TextParseOptions _confirmBoxTextOptions = {
Qt::LayoutDirectionAuto, // dir
};
ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const QString &cancelText) : _infoMsg(false),
_confirm(this, doneText.isEmpty() ? lang(lng_continue) : doneText, st::btnSelectDone),
_cancel(this, cancelText.isEmpty() ? lang(lng_cancel) : cancelText, st::btnSelectCancel),
ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const QString &cancelText, const style::flatButton &doneStyle, const style::flatButton &cancelStyle) : _infoMsg(false),
_confirm(this, doneText.isEmpty() ? lang(lng_continue) : doneText, doneStyle),
_cancel(this, cancelText.isEmpty() ? lang(lng_cancel) : cancelText, cancelStyle),
_close(this, QString(), st::btnInfoClose),
_text(100), _hiding(false), a_opacity(0, 1), af_opacity(anim::linear) {
_text(100) {
init(text);
}
@@ -41,40 +41,32 @@ ConfirmBox::ConfirmBox(const QString &text, bool noDone, const QString &cancelTe
_confirm(this, QString(), st::btnSelectDone),
_cancel(this, QString(), st::btnSelectCancel),
_close(this, cancelText.isEmpty() ? lang(lng_close) : cancelText, st::btnInfoClose),
_text(100), _hiding(false), a_opacity(0, 1), af_opacity(anim::linear) {
_text(100) {
init(text);
}
void ConfirmBox::init(const QString &text) {
_text.setText(st::boxFont, text, (_infoMsg ? _confirmBoxTextOptions : _textPlainOptions));
_width = st::confirmWidth;
_textWidth = _width - st::boxPadding.left() - st::boxPadding.right();
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxPadding.right();
_textHeight = _text.countHeight(_textWidth);
_height = st::boxPadding.top() + _textHeight + st::boxPadding.bottom() + (_infoMsg ? _close.height() : _confirm.height());
setMaxHeight(st::boxPadding.top() + _textHeight + st::boxPadding.bottom() + (_infoMsg ? _close.height() : _confirm.height()));
if (_infoMsg) {
_confirm.hide();
_cancel.hide();
_close.move(0, st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
connect(&_close, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
setMouseTracking(_text.hasLinks());
} else {
_confirm.move(_width - _confirm.width(), st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
_cancel.move(0, st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
_close.hide();
connect(&_confirm, SIGNAL(clicked()), this, SIGNAL(confirmed()));
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
}
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void ConfirmBox::mouseMoveEvent(QMouseEvent *e) {
@@ -122,7 +114,7 @@ void ConfirmBox::updateLink() {
void ConfirmBox::updateHover() {
QPoint m(mapFromGlobal(_lastMousePos));
bool wasMy = (_myLink == textlnkOver());
_myLink = _text.link(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, (_text.maxWidth() < _width) ? style::al_center : style::al_left);
_myLink = _text.link(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, (_text.maxWidth() < width()) ? style::al_center : style::al_left);
if (_myLink != textlnkOver()) {
if (wasMy || _myLink || rect().contains(m)) {
textlnkOver(_myLink);
@@ -132,6 +124,10 @@ void ConfirmBox::updateHover() {
}
}
void ConfirmBox::closePressed() {
emit cancelled();
}
void ConfirmBox::hideAll() {
_confirm.hide();
_cancel.hide();
@@ -150,70 +146,34 @@ void ConfirmBox::showAll() {
void ConfirmBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
emit confirmed();
} else if (e->key() == Qt::Key_Escape) {
onCancel();
} else {
AbstractBox::keyPressEvent(e);
}
}
void ConfirmBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void ConfirmBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
if (paint(p)) return;
if (!_infoMsg) {
// paint shadows
p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
if (!_infoMsg) {
// paint shadows
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
// paint button sep
p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
// draw box title / text
p.setFont(st::boxFont->f);
p.setPen(st::black->p);
_text.draw(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, (_text.maxWidth() < _width) ? style::al_center : style::al_left);
}
// draw box title / text
p.setFont(st::boxFont->f);
p.setPen(st::black->p);
_text.draw(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, (_text.maxWidth() < width()) ? style::al_center : style::al_left);
}
void ConfirmBox::resizeEvent(QResizeEvent *e) {
if (_infoMsg) {
_close.move(0, st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
_confirm.move(width() - _confirm.width(), st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
_cancel.move(0, st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
}
}
void ConfirmBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
setFocus();
}
} else {
a_opacity.update(ms, af_opacity);
}
update();
}
void ConfirmBox::onCancel() {
emit cancelled();
emit closed();
}
void ConfirmBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
ConfirmBox::~ConfirmBox() {
}

View File

@@ -17,35 +17,34 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class ConfirmBox : public LayeredWidget, public RPCSender {
class ConfirmBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
ConfirmBox(const QString &text, const QString &doneText = QString(), const QString &cancelText = QString());
ConfirmBox(const QString &text, const QString &doneText = QString(), const QString &cancelText = QString(), const style::flatButton &doneStyle = st::btnSelectDone, const style::flatButton &cancelStyle = st::btnSelectCancel);
ConfirmBox(const QString &text, bool noDone, const QString &cancelText = QString());
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
void updateLink();
void startHide();
~ConfirmBox();
signals:
void confirmed();
void cancelled();
public slots:
protected:
void onCancel();
void closePressed();
void hideAll();
void showAll();
private:
@@ -53,21 +52,11 @@ private:
bool _infoMsg;
void hideAll();
void showAll();
int32 _width, _height;
FlatButton _confirm, _cancel;
BottomButton _close;
Text _text;
int32 _textWidth, _textHeight;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
anim::transition af_opacity;
void updateHover();
QPoint _lastMousePos;

View File

@@ -33,13 +33,10 @@ ConnectionBox::ConnectionBox() :
_passwordInput(this, st::inpConnectionPassword, lang(lng_connection_password_ph), cConnectionProxy().password),
_autoRadio(this, qsl("conn_type"), dbictAuto, lang(lng_connection_auto_rb), (cConnectionType() == dbictAuto)),
_httpProxyRadio(this, qsl("conn_type"), dbictHttpProxy, lang(lng_connection_http_proxy_rb), (cConnectionType() == dbictHttpProxy)),
_tcpProxyRadio(this, qsl("conn_type"), dbictTcpProxy, lang(lng_connection_tcp_proxy_rb), (cConnectionType() == dbictTcpProxy)),
a_opacity(0, 1), _hiding(false) {
_width = st::addContactWidth;
_tcpProxyRadio(this, qsl("conn_type"), dbictTcpProxy, lang(lng_connection_tcp_proxy_rb), (cConnectionType() == dbictTcpProxy)) {
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_autoRadio, SIGNAL(changed()), this, SLOT(onChange()));
connect(&_httpProxyRadio, SIGNAL(changed()), this, SLOT(onChange()));
@@ -47,9 +44,7 @@ ConnectionBox::ConnectionBox() :
_passwordInput.setEchoMode(QLineEdit::Password);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void ConnectionBox::hideAll() {
@@ -71,7 +66,48 @@ void ConnectionBox::showAll() {
_httpProxyRadio.show();
_tcpProxyRadio.show();
_autoRadio.move(st::boxPadding.left(), st::addContactTitleHeight + st::connectionSkip);
int32 h = st::boxTitleHeight + st::connectionSkip + _autoRadio.height() + st::connectionSkip + _httpProxyRadio.height() + st::connectionSkip + _tcpProxyRadio.height() + st::connectionSkip;
if (_httpProxyRadio.checked() || _tcpProxyRadio.checked()) {
h += 2 * st::boxPadding.top() + 2 * _hostInput.height();
_hostInput.show();
_portInput.show();
_userInput.show();
_passwordInput.show();
} else {
_hostInput.hide();
_portInput.hide();
_userInput.hide();
_passwordInput.hide();
}
_saveButton.show();
_cancelButton.show();
setMaxHeight(h + _saveButton.height());
resizeEvent(0);
}
void ConnectionBox::showDone() {
if (!_hostInput.isHidden()) {
_hostInput.setFocus();
}
}
void ConnectionBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_connection_header), true);
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
void ConnectionBox::resizeEvent(QResizeEvent *e) {
_autoRadio.move(st::boxPadding.left(), st::boxTitleHeight + st::connectionSkip);
_httpProxyRadio.move(st::boxPadding.left(), _autoRadio.y() + _autoRadio.height() + st::connectionSkip);
int32 inputy = 0;
@@ -86,85 +122,16 @@ void ConnectionBox::showAll() {
}
if (inputy) {
_hostInput.show();
_portInput.show();
_userInput.show();
_passwordInput.show();
_hostInput.move(st::boxPadding.left() + st::rbDefFlat.textLeft, inputy);
_portInput.move(_width - st::boxPadding.right() - _portInput.width(), inputy);
_portInput.move(width() - st::boxPadding.right() - _portInput.width(), inputy);
_userInput.move(st::boxPadding.left() + st::rbDefFlat.textLeft, _hostInput.y() + _hostInput.height() + st::boxPadding.top());
_passwordInput.move(_width - st::boxPadding.right() - _passwordInput.width(), _userInput.y());
} else {
_hostInput.hide();
_portInput.hide();
_userInput.hide();
_passwordInput.hide();
_passwordInput.move(width() - st::boxPadding.right() - _passwordInput.width(), _userInput.y());
}
_saveButton.show();
_cancelButton.show();
int32 buttony = (_tcpProxyRadio.checked() ? (_userInput.y() + _userInput.height()) : (_tcpProxyRadio.y() + _tcpProxyRadio.height())) + st::connectionSkip;
_saveButton.move(_width - _saveButton.width(), buttony);
_saveButton.move(width() - _saveButton.width(), buttony);
_cancelButton.move(0, buttony);
_height = _saveButton.y() + _saveButton.height();
resize(_width, _height);
}
void ConnectionBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
} else if (e->key() == Qt::Key_Escape) {
onCancel();
}
}
void ConnectionBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void ConnectionBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
// draw box title / text
p.setFont(st::addContactTitleFont->f);
p.setPen(st::black->p);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_connection_header));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void ConnectionBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
if (!_hostInput.isHidden()) {
_hostInput.setFocus();
}
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
}
void ConnectionBox::onChange() {
@@ -210,19 +177,3 @@ void ConnectionBox::onSave() {
reinitImageLinkManager();
emit closed();
}
void ConnectionBox::onCancel() {
emit closed();
}
void ConnectionBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
ConnectionBox::~ConnectionBox() {
}

View File

@@ -17,42 +17,34 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "gui/phoneinput.h"
#include "abstractbox.h"
class ConnectionBox : public LayeredWidget {
class ConnectionBox : public AbstractBox {
Q_OBJECT
public:
ConnectionBox();
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~ConnectionBox();
void resizeEvent(QResizeEvent *e);
public slots:
void onChange();
void onSave();
void onCancel();
private:
protected:
void hideAll();
void showAll();
void showDone();
private:
FlatButton _saveButton, _cancelButton;
FlatInput _hostInput;
PortInput _portInput;
FlatInput _userInput, _passwordInput;
FlatRadiobutton _autoRadio, _httpProxyRadio, _tcpProxyRadio;
int32 _width, _height;
QPixmap _cache;
anim::fvalue a_opacity;
bool _hiding;
};

File diff suppressed because it is too large Load Diff

View File

@@ -17,14 +17,20 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class ContactsInner : public QWidget, public RPCSender {
Q_OBJECT
private:
struct ContactData;
public:
ContactsInner();
ContactsInner(bool creatingChat);
ContactsInner(ChatData *chat);
void init();
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
@@ -33,19 +39,35 @@ public:
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void paintDialog(QPainter &p, DialogRow *row, bool sel);
void paintDialog(QPainter &p, UserData *user, ContactData *data, bool sel);
void updateFilter(QString filter = QString());
void selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir);
QVector<UserData*> selected();
QVector<MTPInputUser> selectedInputs();
PeerData *selectedUser();
void loadProfilePhotos(int32 yFrom);
void chooseParticipant();
void changeCheckState(DialogRow *row);
void changeCheckState(ContactData *data);
void peopleReceived(const QString &query, const QVector<MTPContactFound> &people);
void refresh();
ChatData *chat() const;
bool creatingChat() const;
~ContactsInner();
signals:
void mustScrollTo(int ymin, int ymax);
void selectAllQuery();
void searchByUsername();
public slots:
@@ -54,10 +76,11 @@ public slots:
void updateSel();
void peerUpdated(PeerData *peer);
void chooseParticipant();
private:
ChatData *_chat;
bool _creatingChat;
int32 _time;
DialogsIndexed *_contacts;
@@ -68,58 +91,116 @@ private:
int32 _filteredSel;
bool _mouseSel;
typedef struct {
int32 _selCount;
struct ContactData {
Text name;
QString online;
} ContactData;
bool inchat;
bool check;
};
typedef QMap<UserData*, ContactData*> ContactsData;
ContactsData _contactsData;
ContactData *contactData(DialogRow *row);
bool _searching;
QString _lastQuery;
typedef QVector<UserData*> ByUsernameRows;
typedef QVector<ContactData*> ByUsernameDatas;
ByUsernameRows _byUsername, _byUsernameFiltered;
ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas
ByUsernameDatas _byUsernameDatas;
int32 _byUsernameSel;
QPoint _lastMousePos;
LinkButton _addContactLnk;
};
class ContactsBox : public LayeredWidget, public RPCSender {
class ContactsBox : public ItemListBox, public RPCSender {
Q_OBJECT
public:
ContactsBox();
void parentResized();
void animStep(float64 dt);
ContactsBox(bool creatingChat = false);
ContactsBox(ChatData *chat);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void startHide();
~ContactsBox();
public slots:
void onFilterUpdate();
void onClose();
void onScroll();
void onAdd();
private:
void onAdd();
void onInvite();
void onNext();
bool onSearchByUsername(bool searchCache = false);
void onNeedSearchByUsername();
protected:
void hideAll();
void showAll();
void showDone();
void created(const MTPmessages_StatedMessage &result);
bool failed(const RPCError &e);
private:
void init();
ScrollArea _scroll;
ContactsInner _inner;
FlatButton _addContact;
int32 _width, _height;
FlatInput _filter;
BottomButton _close;
bool _hiding;
QPixmap _cache;
FlatButton _next, _cancel;
anim::fvalue a_opacity;
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req);
bool peopleFailed(const RPCError &error, mtpRequestId req);
QTimer _searchTimer;
QString _peopleQuery;
bool _peopleFull;
mtpRequestId _peopleRequest;
typedef QMap<QString, MTPcontacts_Found> PeopleCache;
PeopleCache _peopleCache;
typedef QMap<mtpRequestId, QString> PeopleQueries;
PeopleQueries _peopleQueries;
};
class CreateGroupBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
CreateGroupBox(const MTPVector<MTPInputUser> &users);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
public slots:
void onCreate();
protected:
void hideAll();
void showAll();
void showDone();
private:
void created(const MTPUpdates &updates);
bool failed(const RPCError &e);
MTPVector<MTPInputUser> _users;
int32 _createRequestId;
FlatInput _name;
FlatButton _create, _cancel;
};

View File

@@ -30,13 +30,10 @@ DownloadPathBox::DownloadPathBox() :
_dirRadio(this, qsl("dir_type"), 2, lang(lng_download_path_dir_radio), !_path.isEmpty() && _path != qsl("tmp")),
_dirInput(this, st::inpDownloadDir, QString(), (_path.isEmpty() || _path == qsl("tmp")) ? QString() : QDir::toNativeSeparators(_path)),
_saveButton(this, lang(lng_connection_save), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
a_opacity(0, 1), _hiding(false) {
_width = st::addContactWidth;
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel) {
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_defaultRadio, SIGNAL(changed()), this, SLOT(onChange()));
connect(&_tempRadio, SIGNAL(changed()), this, SLOT(onChange()));
@@ -45,9 +42,7 @@ DownloadPathBox::DownloadPathBox() :
connect(&_dirInput, SIGNAL(focused()), this, SLOT(onEditPath()));
_dirInput.setCursorPosition(0);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void DownloadPathBox::hideAll() {
@@ -75,71 +70,38 @@ void DownloadPathBox::showAll() {
_saveButton.show();
_cancelButton.show();
_defaultRadio.move(st::boxPadding.left(), st::addContactTitleHeight + st::downloadSkip);
int32 h = st::boxTitleHeight + st::downloadSkip + _defaultRadio.height() + st::downloadSkip + _tempRadio.height() + st::downloadSkip + _dirRadio.height();
if (_dirRadio.checked()) h += st::boxPadding.top() + _dirInput.height();
h += st::downloadSkip + _saveButton.height();
setMaxHeight(h);
}
void DownloadPathBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_download_path_header), true);
// paint shadows
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
void DownloadPathBox::resizeEvent(QResizeEvent *e) {
_defaultRadio.move(st::boxPadding.left(), st::boxTitleHeight + st::downloadSkip);
_tempRadio.move(st::boxPadding.left(), _defaultRadio.y() + _defaultRadio.height() + st::downloadSkip);
_dirRadio.move(st::boxPadding.left(), _tempRadio.y() + _tempRadio.height() + st::downloadSkip);
int32 inputy = _dirRadio.y() + _dirRadio.height() + st::boxPadding.top();
_dirInput.move(st::boxPadding.left() + st::rbDefFlat.textLeft, inputy);
int32 buttony = (_dirRadio.checked() ? (_dirInput.y() + _dirInput.height()) : (_dirRadio.y() + _dirRadio.height())) + st::downloadSkip;
_saveButton.move(_width - _saveButton.width(), buttony);
_saveButton.move(width() - _saveButton.width(), buttony);
_cancelButton.move(0, buttony);
_height = _saveButton.y() + _saveButton.height();
resize(_width, _height);
}
void DownloadPathBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
} else if (e->key() == Qt::Key_Escape) {
onCancel();
}
}
void DownloadPathBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void DownloadPathBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
// draw box title / text
p.setFont(st::addContactTitleFont->f);
p.setPen(st::black->p);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_download_path_header));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void DownloadPathBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
}
void DownloadPathBox::onChange() {
@@ -186,19 +148,3 @@ void DownloadPathBox::onSave() {
Local::writeUserSettings();
emit closed();
}
void DownloadPathBox::onCancel() {
emit closed();
}
void DownloadPathBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
DownloadPathBox::~DownloadPathBox() {
}

View File

@@ -17,43 +17,33 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "gui/phoneinput.h"
#include "abstractbox.h"
class DownloadPathBox : public LayeredWidget {
class DownloadPathBox : public AbstractBox {
Q_OBJECT
public:
DownloadPathBox();
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~DownloadPathBox();
void resizeEvent(QResizeEvent *e);
public slots:
void onChange();
void onEditPath();
void onSave();
void onCancel();
private:
protected:
void hideAll();
void showAll();
private:
QString _path;
FlatRadiobutton _defaultRadio, _tempRadio, _dirRadio;
FlatInput _dirInput;
FlatButton _saveButton, _cancelButton;
int32 _width, _height;
QPixmap _cache;
anim::fvalue a_opacity;
bool _hiding;
};

View File

@@ -68,31 +68,16 @@ namespace {
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 7;
}
EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton),
_hiding(false), a_opacity(0, 1) {
EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton) {
fillBlocks();
_blockHeight = st::emojiReplaceInnerHeight;
_width = _blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - st::emojiSize);
_height = st::boxPadding.top() + st::boxFont->height;
_height += _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight);
_height += _done.height();
_done.setWidth(_width);
_header.setText(st::boxFont, lang(lng_settings_emoji_list));
_done.move(0, _height - _done.height());
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());
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void EmojiBox::fillBlocks() {
@@ -122,77 +107,35 @@ void EmojiBox::showAll() {
void EmojiBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
onClose();
} else if (e->key() == Qt::Key_Escape) {
onClose();
} else {
AbstractBox::keyPressEvent(e);
}
}
void EmojiBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void EmojiBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
if (paint(p)) return;
p.setFont(st::boxFont->f);
p.setPen(st::boxGrayTitle->p);
_header.draw(p, 0, st::boxPadding.top(), _width, Qt::AlignCenter);
paintGrayTitle(p, lang(lng_settings_emoji_list));
p.setFont(st::emojiTextFont->f);
p.setPen(st::black->p);
int32 top = st::boxPadding.top() + st::boxFont->height + (st::emojiReplaceHeight - _blockHeight) / 2;
for (Blocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
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));
}
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));
left += st::emojiReplaceWidth;
}
top += st::emojiReplaceHeight;
p.setFont(st::emojiTextFont->f);
p.setPen(st::black->p);
int32 top = st::boxPadding.top() + st::boxFont->height + (st::emojiReplaceHeight - _blockHeight) / 2;
for (Blocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
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));
}
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));
left += st::emojiReplaceWidth;
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
top += st::emojiReplaceHeight;
}
}
void EmojiBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
setFocus();
}
} else {
a_opacity.update(ms, anim::linear);
}
update();
}
void EmojiBox::onClose() {
emit closed();
}
void EmojiBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
EmojiBox::~EmojiBox() {
void EmojiBox::resizeEvent(QResizeEvent *e) {
_done.setGeometry(0, height() - _done.height(), width(), _done.height());
}

View File

@@ -17,37 +17,29 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class EmojiBox : public LayeredWidget {
class EmojiBox : public AbstractBox {
Q_OBJECT
public:
EmojiBox();
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~EmojiBox();
void resizeEvent(QResizeEvent *e);
public slots:
void onClose();
private:
protected:
void hideAll();
void showAll();
private:
void fillBlocks();
int32 _width, _height;
BottomButton _done;
Text _header;
int32 _blockHeight;
struct Block {
Block(const EmojiData *emoji = 0, const QString &text = QString()) : emoji(emoji), text(text) {
@@ -58,9 +50,4 @@ private:
typedef QVector<Block> BlockRow;
typedef QVector<BlockRow> Blocks;
Blocks _blocks;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};

View File

@@ -28,15 +28,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "langloaderplain.h"
LanguageBox::LanguageBox() :
_done(this, lang(lng_about_done), st::langsCloseButton),
_hiding(false), a_opacity(0, 1) {
_done(this, lang(lng_about_done), st::langsCloseButton) {
bool haveTestLang = (cLang() == languageTest);
_width = st::langsWidth;
_height = st::addContactTitleHeight + st::langsPadding.top() + st::langsPadding.bottom() + (languageCount + (haveTestLang ? 1 : 0)) * (st::langPadding.top() + st::rbDefFlat.height + st::langPadding.bottom()) + _done.height();
int32 y = st::addContactTitleHeight + st::langsPadding.top();
int32 y = st::boxTitleHeight + st::langsPadding.top();
_langs.reserve(languageCount + (haveTestLang ? 1 : 0));
if (haveTestLang) {
_langs.push_back(new FlatRadiobutton(this, qsl("lang"), languageTest, qsl("Custom Lang"), (cLang() == languageTest), st::langButton));
@@ -58,15 +54,11 @@ _hiding(false), a_opacity(0, 1) {
connect(_langs.back(), SIGNAL(changed()), this, SLOT(onChange()));
}
_done.move(0, _height - _done.height());
resizeMaxHeight(st::langsWidth, st::boxTitleHeight + st::langsPadding.top() + st::langsPadding.bottom() + (languageCount + (haveTestLang ? 1 : 0)) * (st::langPadding.top() + st::rbDefFlat.height + st::langPadding.bottom()) + _done.height());
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void LanguageBox::hideAll() {
@@ -83,12 +75,6 @@ void LanguageBox::showAll() {
}
}
void LanguageBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
onClose();
}
}
void LanguageBox::mousePressEvent(QMouseEvent *e) {
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
for (int32 i = 1; i < languageCount; ++i) {
@@ -107,45 +93,15 @@ void LanguageBox::mousePressEvent(QMouseEvent *e) {
}
}
void LanguageBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void LanguageBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(0, 0, _width, _height, st::boxBG->b);
Painter p(this);
if (paint(p)) return;
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
// draw box title / text
p.setFont(st::addContactTitleFont->f);
p.setPen(st::black->p);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_languages));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
paintTitle(p, lang(lng_languages), true);
}
void LanguageBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
setFocus();
}
} else {
a_opacity.update(ms, anim::linear);
}
update();
void LanguageBox::resizeEvent(QResizeEvent *e) {
_done.move(0, height() - _done.height());
}
void LanguageBox::onChange() {
@@ -193,19 +149,6 @@ void LanguageBox::onSave() {
}
}
void LanguageBox::onClose() {
emit closed();
}
void LanguageBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
LanguageBox::~LanguageBox() {
for (int32 i = 0, l = _langs.size(); i < l; ++i) {
delete _langs[i];

View File

@@ -17,20 +17,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class LanguageBox : public LayeredWidget {
class LanguageBox : public AbstractBox {
Q_OBJECT
public:
LanguageBox();
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void mousePressEvent(QMouseEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
void resizeEvent(QResizeEvent *e);
~LanguageBox();
public slots:
@@ -38,19 +35,14 @@ public slots:
void onChange();
void onRestore();
void onSave();
void onClose();
private:
protected:
void hideAll();
void showAll();
private:
QVector<FlatRadiobutton*> _langs;
int32 _width, _height;
BottomButton _done;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};

View File

@@ -1,800 +0,0 @@
/*
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 "newgroupbox.h"
#include "mainwidget.h"
#include "window.h"
NewGroupInner::NewGroupInner() :
_contacts(&App::main()->contactsList()),
_sel(0),
_filteredSel(-1),
_mouseSel(false),
_selCount(0),
_addContactLnk(this, lang(lng_add_contact_button)) {
connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) {
r->attached = 0;
}
_filter = qsl("a");
updateFilter();
for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) {
r->attached = 0;
}
connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow *, DialogRow *)), this, SLOT(onDialogRowReplaced(DialogRow *, DialogRow *)));
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData *)));
connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(peerUpdated(PeerData *)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
}
void NewGroupInner::peerUpdated(PeerData *peer) {
if (!peer->chat) {
ContactsData::iterator i = _contactsData.find(peer->asUser());
if (i != _contactsData.cend()) {
for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
if (row->attached == i.value()) row->attached = 0;
}
if (!_filter.isEmpty()) {
for (int32 j = 0, s = _filtered.size(); j < s; ++j) {
if (_filtered[j]->attached == i.value()) _filtered[j]->attached = 0;
}
}
delete i.value();
_contactsData.erase(i);
}
}
parentWidget()->update();
}
void NewGroupInner::loadProfilePhotos(int32 yFrom) {
int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5;
MTP::clearLoaderPriorities();
if (yTo < 0) return;
if (yFrom < 0) yFrom = 0;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
_contacts->list.adjustCurrent(yFrom, rh);
for (
DialogRow *preloadFrom = _contacts->list.current;
preloadFrom != _contacts->list.end && preloadFrom->pos * rh < yTo;
preloadFrom = preloadFrom->next
) {
preloadFrom->history->peer->photo->load();
}
}
} else if (!_filtered.isEmpty()) {
int32 from = yFrom / rh;
if (from < 0) from = 0;
if (from < _filtered.size()) {
int32 to = (yTo / rh) + 1;
if (to > _filtered.size()) to = _filtered.size();
for (; from < to; ++from) {
_filtered[from]->history->peer->photo->load();
}
}
}
}
NewGroupInner::ContactData *NewGroupInner::contactData(DialogRow *row) {
ContactData *data = (ContactData*)row->attached;
if (!data) {
UserData *user = row->history->peer->asUser();
ContactsData::const_iterator i = _contactsData.constFind(user);
if (i == _contactsData.cend()) {
_contactsData.insert(user, data = new ContactData());
data->check = false;
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
data->online = App::onlineText(user, _time);
} else {
data = i.value();
}
row->attached = data;
}
return data;
}
void NewGroupInner::paintDialog(QPainter &p, DialogRow *row, bool sel) {
int32 left = st::profileListPadding.width();
UserData *user = row->history->peer->asUser();
ContactData *data = contactData(row);
if (_selCount >= cMaxGroupCount() && !data->check) {
sel = false;
}
if (sel || data->check) {
p.fillRect(0, 0, width(), 2 * st::profileListPadding.height() + st::profileListPhotoSize, (data->check ? st::profileActiveBG : st::profileHoverBG)->b);
}
p.drawPixmap(left, st::profileListPadding.height(), user->photo->pix(st::profileListPhotoSize));
if (data->check) {
p.setPen(st::white->p);
} else {
p.setPen(st::profileListNameColor->p);
}
data->name.drawElided(p, left + st::profileListPhotoSize + st::participantDelta, st::profileListNameTop, width() - st::profileListPadding.width() - st::profileListPhotoSize - st::profileListPadding.width() - st::participantDelta - st::scrollDef.width - st::profileCheckRect.pxWidth());
if (sel || data->check) {
p.drawPixmap(QPoint(width() - st::profileCheckRect.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::profileCheckRect.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), (data->check ? st::profileCheckActiveRect : st::profileCheckRect));
}
p.setFont(st::profileSubFont->f);
if (data->check) {
p.setPen(st::white->p);
} else {
p.setPen((App::onlineColorUse(user->onlineTill, _time) ? st::profileOnlineColor : st::profileOfflineColor)->p);
}
p.drawText(left + st::profileListPhotoSize + st::participantDelta, st::profileListPadding.height() + st::profileListPhotoSize - 6, data->online);
}
void NewGroupInner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
QPainter p(this);
_time = unixtime();
p.fillRect(r, st::white->b);
int32 yFrom = r.top();
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
if (_filter.isEmpty()) {
if (_contacts->list.count) {
_contacts->list.adjustCurrent(yFrom, rh);
DialogRow *drawFrom = _contacts->list.current;
p.translate(0, drawFrom->pos * rh);
while (drawFrom != _contacts->list.end && drawFrom->pos * rh < r.bottom()) {
paintDialog(p, drawFrom, (drawFrom == _sel));
p.translate(0, rh);
drawFrom = drawFrom->next;
}
} else {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
}
} else {
if (_filtered.isEmpty()) {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_not_found), style::al_center);
} else {
int32 from = yFrom / rh;
if (from < 0) from = 0;
if (from < _filtered.size()) {
int32 to = (r.bottom() / rh) + 1;
if (to > _filtered.size()) to = _filtered.size();
p.translate(0, from * rh);
for (; from < to; ++from) {
paintDialog(p, _filtered[from], (_filteredSel == from));
p.translate(0, rh);
}
}
}
}
}
void NewGroupInner::enterEvent(QEvent *e) {
setMouseTracking(true);
}
void NewGroupInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
updateSel();
}
void NewGroupInner::mouseMoveEvent(QMouseEvent *e) {
_mouseSel = true;
_lastMousePos = e->globalPos();
updateSel();
}
void NewGroupInner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true;
_lastMousePos = e->globalPos();
updateSel();
if (e->button() == Qt::LeftButton) {
chooseParticipant();
}
}
void NewGroupInner::changeCheckState(DialogRow *row) {
if (contactData(row)->check) {
contactData(row)->check = false;
--_selCount;
} else if (_selCount < cMaxGroupCount()) {
contactData(row)->check = true;
++_selCount;
}
}
void NewGroupInner::chooseParticipant() {
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
if (_filter.isEmpty()) {
if (!_sel) return;
changeCheckState(_sel);
} else {
if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return;
DialogRow *row = _filtered[_filteredSel];
changeCheckState(row);
PeerData *peer = row->history->peer;
emit selectAllQuery();
}
parentWidget()->update();
}
void NewGroupInner::updateSel() {
if (!_mouseSel) return;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
QPoint p(mapFromGlobal(_lastMousePos));
if (_filter.isEmpty()) {
DialogRow *newSel = rect().contains(p) ? _contacts->list.rowAtY(p.y(), rh) : 0;
if (newSel != _sel) {
_sel = newSel;
parentWidget()->update();
}
} else {
int32 newFilteredSel = (p.y() >= 0 && rect().contains(p)) ? (p.y() / rh) : -1;
if (newFilteredSel != _filteredSel) {
_filteredSel = newFilteredSel;
parentWidget()->update();
}
}
}
void NewGroupInner::updateFilter(QString filter) {
filter = textSearchKey(filter);
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) {
int32 rh = (st::profileListPhotoSize + st::profileListPadding.height() * 2);
_filter = filter;
if (_filter.isEmpty()) {
resize(width(), _contacts->list.count * rh);
if (_contacts->list.count) {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
resize(width(), _contacts->list.count * rh);
_sel = _contacts->list.begin;
} else {
resize(width(), st::noContactsHeight);
if (cContactsReceived()) {
if (_addContactLnk.isHidden()) _addContactLnk.show();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
}
}
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
_filtered.clear();
if (!f.isEmpty()) {
DialogsList *dialogsToFilter = 0;
if (_contacts->list.count) {
for (fi = fb; fi != fe; ++fi) {
DialogsIndexed::DialogsIndex::iterator i = _contacts->index.find(fi->at(0));
if (i == _contacts->index.cend()) {
dialogsToFilter = 0;
break;
}
if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) {
dialogsToFilter = i.value();
}
}
}
if (dialogsToFilter && dialogsToFilter->count) {
_filtered.reserve(dialogsToFilter->count);
for (DialogRow *i = dialogsToFilter->begin, *e = dialogsToFilter->end; i != e; i = i->next) {
const PeerData::Names &names(i->history->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (ni == ne) {
break;
}
}
if (fi == fe) {
i->attached = 0;
_filtered.push_back(i);
}
}
}
}
_filteredSel = _filtered.isEmpty() ? -1 : 0;
if (!_filtered.isEmpty()) {
resize(width(), _filtered.size() * rh);
} else {
resize(width(), st::noContactsHeight);
}
}
if (parentWidget()) parentWidget()->update();
loadProfilePhotos(0);
}
}
void NewGroupInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
if (!_filter.isEmpty()) {
for (FilteredDialogs::iterator i = _filtered.begin(), e = _filtered.end(); i != e;) {
if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts!
if (newRow) {
*i = newRow;
++i;
} else {
i = _filtered.erase(i);
}
} else {
++i;
}
}
if (_filteredSel >= _filtered.size()) {
_filteredSel = -1;
}
} else {
if (_sel == oldRow) {
_sel = newRow;
}
}
_mouseSel = false;
int32 rh = (st::profileListPhotoSize + st::profileListPadding.height() * 2);
int32 newh = (_filter.isEmpty() ? _contacts->list.count : _filtered.size()) * rh;
resize(width(), newh);
}
NewGroupInner::~NewGroupInner() {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
delete *i;
}
}
void NewGroupInner::resizeEvent(QResizeEvent *e) {
_addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2);
}
void NewGroupInner::selectSkip(int32 dir) {
_mouseSel = false;
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, origDir = dir;
if (_filter.isEmpty()) {
if (_sel) {
if (dir > 0) {
while (dir && _sel->next->next) {
_sel = _sel->next;
--dir;
}
} else {
while (dir && _sel->prev) {
_sel = _sel->prev;
++dir;
}
}
} else if (dir > 0 && _contacts->list.count) {
_sel = _contacts->list.begin;
}
if (_sel) {
emit mustScrollTo(_sel->pos * rh, (_sel->pos + 1) * rh);
}
} else {
if (dir > 0) {
if (_filteredSel < 0 && dir > 1) {
_filteredSel = 0;
}
_filteredSel += dir;
if (_filteredSel >= _filtered.size()) {
_filteredSel = _filtered.size() - 1;
}
} else if (_filteredSel > 0) {
_filteredSel += dir;
if (_filteredSel < 0) {
_filteredSel = 0;
}
}
if (_filteredSel >= 0) {
emit mustScrollTo(_filteredSel * rh, (_filteredSel + 1) * rh);
}
}
parentWidget()->update();
}
void NewGroupInner::selectSkipPage(int32 h, int32 dir) {
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2;
int32 points = h / rh;
if (!points) return;
selectSkip(points * dir);
}
QVector<MTPInputUser> NewGroupInner::selectedInputs() {
QVector<MTPInputUser> result;
result.reserve(_contactsData.size());
for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) {
if (i.value()->check) {
result.push_back(i.key()->inputUser);
}
}
return result;
}
PeerData *NewGroupInner::selectedUser() {
for (ContactsData::const_iterator i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) {
if (i.value()->check) {
return i.key();
}
}
return 0;
}
NewGroupBox::NewGroupBox() : _scroll(this, st::newGroupScroll), _inner(),
_filter(this, st::contactsFilter, lang(lng_participant_filter)),
_next(this, lang(lng_create_group_next), st::btnSelectDone),
_cancel(this, lang(lng_cancel), st::btnSelectCancel),
_hiding(false), a_opacity(0, 1) {
_width = st::participantWidth;
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
if (_height > st::participantMaxHeight) _height = st::participantMaxHeight;
resize(_width, _height);
_scroll.setWidget(&_inner);
_scroll.setFocusPolicy(Qt::NoFocus);
connect(&_next, SIGNAL(clicked()), this, SLOT(onNext()));
connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(closed()));
connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSel()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
connect(&_filter, SIGNAL(cancelled()), this, SLOT(onClose()));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll()));
showAll();
_cache = myGrab(this, rect());
hideAll();
}
void NewGroupBox::hideAll() {
_filter.hide();
_scroll.hide();
_next.hide();
_cancel.hide();
}
void NewGroupBox::showAll() {
_filter.show();
_scroll.show();
_next.show();
_cancel.show();
}
void NewGroupBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
onClose();
} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
_inner.chooseParticipant();
} else if (_filter.hasFocus()) {
if (e->key() == Qt::Key_Down) {
_inner.selectSkip(1);
} else if (e->key() == Qt::Key_Up) {
_inner.selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner.selectSkipPage(_scroll.height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner.selectSkipPage(_scroll.height(), -1);
} else {
e->ignore();
}
} else {
e->ignore();
}
}
void NewGroupBox::parentResized() {
QSize s = parentWidget()->size();
_height = App::wnd()->height() - st::boxPadding.top() - st::boxPadding.bottom();
if (_height > st::participantMaxHeight) _height = st::participantMaxHeight;
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void NewGroupBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
// paint shadows
p.fillRect(0, st::participantFilter.height, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_create_new_group));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void NewGroupBox::resizeEvent(QResizeEvent *e) {
LayeredWidget::resizeEvent(e);
_filter.move(st::newGroupNamePadding.left(), st::contactsAdd.height + st::newGroupNamePadding.top());
_inner.resize(_width, _inner.height());
_scroll.resize(_width, _height - st::contactsAdd.height - st::newGroupNamePadding.top() - _filter.height() - st::newGroupNamePadding.bottom() - _cancel.height());
_scroll.move(0, _filter.y() + _filter.height() + st::newGroupNamePadding.bottom());
_next.move(width() - _next.width(), _height - _next.height());
_cancel.move(0, _height - _cancel.height());
}
void NewGroupBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
_filter.setFocus();
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
}
void NewGroupBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
void NewGroupBox::onFilterUpdate() {
_scroll.scrollToY(0);
_inner.updateFilter(_filter.text());
}
void NewGroupBox::onClose() {
emit closed();
}
void NewGroupBox::onNext() {
MTPVector<MTPInputUser> users(MTP_vector<MTPInputUser>(_inner.selectedInputs()));
const QVector<MTPInputUser> &v(users.c_vector().v);
if (v.isEmpty()) {
_filter.setFocus();
_filter.notaBene();
} else if (v.size() == 1) {
App::main()->showPeer(_inner.selectedUser()->id);
} else {
App::wnd()->replaceLayer(new CreateGroupBox(users));
}
}
void NewGroupBox::onScroll() {
_inner.loadProfilePhotos(_scroll.scrollTop());
}
NewGroupBox::~NewGroupBox() {
}
CreateGroupBox::CreateGroupBox(const MTPVector<MTPInputUser> &users) : _users(users),
_createRequestId(0),
_name(this, st::newGroupName, lang(lng_dlg_new_group_name)),
_create(this, lang(lng_dlg_create_group), st::btnSelectDone),
_cancel(this, lang(lng_cancel), st::btnSelectCancel),
_hiding(false), a_opacity(0, 1) {
_width = st::addContactWidth;
_height = st::addContactTitleHeight + st::addContactPadding.top() + _name.height() + st::addContactPadding.bottom() + _create.height();
_name.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _name.height());
int32 buttonTop = _name.y() + _name.height() + st::addContactPadding.bottom();
_cancel.move(0, buttonTop);
_create.move(_width - _create.width(), buttonTop);
connect(&_create, SIGNAL(clicked()), this, SLOT(onCreate()));
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onCancel()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
}
void CreateGroupBox::hideAll() {
_name.hide();
_cancel.hide();
_create.hide();
}
void CreateGroupBox::showAll() {
_name.show();
_cancel.show();
_create.show();
}
void CreateGroupBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_name.hasFocus()) {
if (_name.text().trimmed().isEmpty()) {
_name.setFocus();
_name.notaBene();
} else {
onCreate();
}
}
} else if (e->key() == Qt::Key_Escape) {
onCancel();
}
}
void CreateGroupBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void CreateGroupBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_create_group_title));
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
}
}
void CreateGroupBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
_name.setFocus();
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
}
void CreateGroupBox::onCreate() {
if (_createRequestId) return;
QString name = _name.text();
if (name.isEmpty()) {
_name.setFocus();
_name.notaBene();
return;
}
_create.setDisabled(true);
_name.setDisabled(true);
_createRequestId = MTP::send(MTPmessages_CreateChat(_users, MTP_string(_name.text())), rpcDone(&CreateGroupBox::created), rpcFail(&CreateGroupBox::failed));
}
void CreateGroupBox::created(const MTPmessages_StatedMessage &result) {
App::main()->sentFullDataReceived(0, result);
const QVector<MTPChat> *d = 0;
switch (result.type()) {
case mtpc_messages_statedMessage: {
d = &result.c_messages_statedMessage().vchats.c_vector().v;
} break;
case mtpc_messages_statedMessageLink: {
d = &result.c_messages_statedMessageLink().vchats.c_vector().v;
} break;
}
App::wnd()->hideLayer();
PeerId peerId = 0;
if (d && !d->isEmpty()) {
switch (d->first().type()) {
case mtpc_chat: peerId = App::peerFromChat(d->first().c_chat().vid); break;
case mtpc_chatForbidden: peerId = App::peerFromChat(d->first().c_chatForbidden().vid); break;
case mtpc_chatEmpty: peerId = App::peerFromChat(d->first().c_chatEmpty().vid); break;
}
}
if (peerId) {
App::main()->showPeer(peerId);
}
}
bool CreateGroupBox::failed(const RPCError &e) {
_createRequestId = 0;
if (e.type() == "NO_CHAT_TITLE") {
_name.setFocus();
return true;
} else if (e.type() == "USERS_TOO_FEW") {
emit closed();
return true;
}
return false;
}
void CreateGroupBox::onCancel() {
emit closed();
}
void CreateGroupBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
CreateGroupBox::~CreateGroupBox() {
}

View File

@@ -1,170 +0,0 @@
/*
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 "layerwidget.h"
class NewGroupInner : public QWidget {
Q_OBJECT
public:
NewGroupInner();
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void paintDialog(QPainter &p, DialogRow *row, bool sel);
void updateFilter(QString filter = QString());
void selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir);
QVector<MTPInputUser> selectedInputs();
PeerData *selectedUser();
void loadProfilePhotos(int32 yFrom);
void changeCheckState(DialogRow *row);
~NewGroupInner();
signals:
void mustScrollTo(int ymin, int ymax);
void selectAllQuery();
public slots:
void onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow);
void updateSel();
void peerUpdated(PeerData *peer);
void chooseParticipant();
private:
int32 _time;
DialogsIndexed *_contacts;
DialogRow *_sel;
QString _filter;
typedef QVector<DialogRow*> FilteredDialogs;
FilteredDialogs _filtered;
int32 _filteredSel;
bool _mouseSel;
typedef struct {
Text name;
QString online;
bool check;
} ContactData;
typedef QMap<UserData*, ContactData*> ContactsData;
ContactsData _contactsData;
int32 _selCount;
ContactData *contactData(DialogRow *row);
QPoint _lastMousePos;
LinkButton _addContactLnk;
};
class NewGroupBox : public LayeredWidget, public RPCSender {
Q_OBJECT
public:
NewGroupBox();
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void startHide();
~NewGroupBox();
public slots:
void onFilterUpdate();
void onClose();
void onNext();
void onScroll();
private:
void hideAll();
void showAll();
ScrollArea _scroll;
NewGroupInner _inner;
int32 _width, _height;
FlatInput _filter;
FlatButton _next, _cancel;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};
class CreateGroupBox : public LayeredWidget, public RPCSender {
Q_OBJECT
public:
CreateGroupBox(const MTPVector<MTPInputUser> &users);
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~CreateGroupBox();
public slots:
void onCreate();
void onCancel();
private:
void hideAll();
void showAll();
void created(const MTPmessages_StatedMessage &result);
bool failed(const RPCError &e);
MTPVector<MTPInputUser> _users;
int32 _createRequestId;
int32 _width, _height;
FlatInput _name;
FlatButton _create, _cancel;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
};

View File

@@ -19,93 +19,130 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "passcodebox.h"
#include "confirmbox.h"
#include "window.h"
#include "localstorage.h"
PasscodeBox::PasscodeBox(bool turningOff) : _turningOff(turningOff),
_about(st::addContactWidth - st::addContactPadding.left() - st::addContactPadding.right()),
_saveButton(this, lang(lng_settings_save), st::btnSelectDone),
PasscodeBox::PasscodeBox(bool turningOff) : _replacedBy(0), _turningOff(turningOff), _cloudPwd(false),
_setRequest(0), _hasRecovery(false), _aboutHeight(0),
_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)),
a_opacity(0, 1), _hiding(false) {
_passwordHint(this, st::inpAddContact, lang(lng_cloud_password_hint)),
_recoverEmail(this, st::inpAddContact, lang(lng_cloud_password_email)),
_recover(this, lang(lng_signin_recover)) {
init();
prepare();
}
_width = st::addContactWidth;
_about.setRichText(st::usernameFont, lang(lng_passcode_about));
int32 aboutHeight = _about.countHeight(_width - st::addContactPadding.left() - st::addContactPadding.right());
PasscodeBox::PasscodeBox(const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff) : _replacedBy(0), _turningOff(turningOff), _cloudPwd(true),
_setRequest(0), _newSalt(newSalt), _curSalt(curSalt), _hasRecovery(hasRecovery), _hint(hint), _aboutHeight(0),
_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(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)),
_recover(this, lang(lng_signin_recover)) {
init();
prepare();
}
void PasscodeBox::init() {
_about.setRichText(st::usernameFont, lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about));
if (!_hint.isEmpty()) _hintText.setText(st::usernameFont, lng_signin_hint(lt_password_hint, _hint));
_aboutHeight = _about.countHeight(st::boxWidth - st::addContactPadding.left() - st::addContactPadding.right());
_oldPasscode.setEchoMode(QLineEdit::Password);
_newPasscode.setEchoMode(QLineEdit::Password);
_reenterPasscode.setEchoMode(QLineEdit::Password);
if (turningOff) {
if (_turningOff) {
_oldPasscode.show();
_boxTitle = lang(lng_passcode_remove);
_height = st::addContactTitleHeight + st::addContactPadding.top() + 1 * _oldPasscode.height() + st::usernameSkip + aboutHeight + st::addContactPadding.bottom() + _saveButton.height();
_boxTitle = lang(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove);
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 1 * _oldPasscode.height() + st::usernameSkip + _aboutHeight + (_hasRecovery ? ((st::usernameSkip + _recover.height()) / 2) : 0) + st::addContactPadding.bottom() + _saveButton.height());
} else {
if (cHasPasscode()) {
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : cHasPasscode();
if (has) {
_oldPasscode.show();
_boxTitle = lang(lng_passcode_change);
_height = st::addContactTitleHeight + st::addContactPadding.top() + 3 * _oldPasscode.height() + st::usernameSkip * 2 + 1 * st::addContactDelta + aboutHeight + st::addContactPadding.bottom() + _saveButton.height();
_boxTitle = lang(_cloudPwd ? lng_cloud_password_change : lng_passcode_change);
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 3 * _oldPasscode.height() + st::usernameSkip * 2 + 1 * st::addContactDelta + (_cloudPwd ? _passwordHint.height() + st::addContactDelta : 0) + _aboutHeight + (_hasRecovery ? ((st::usernameSkip + _recover.height()) / 2) : 0) + st::addContactPadding.bottom() + _saveButton.height());
} else {
_oldPasscode.hide();
_boxTitle = lang(lng_passcode_create);
_height = st::addContactTitleHeight + st::addContactPadding.top() + 2 * _oldPasscode.height() + st::usernameSkip + 1 * st::addContactDelta + aboutHeight + st::addContactPadding.bottom() + _saveButton.height();
_boxTitle = lang(_cloudPwd ? lng_cloud_password_create : lng_passcode_create);
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 2 * _oldPasscode.height() + st::usernameSkip + 1 * st::addContactDelta + (_cloudPwd ? _passwordHint.height() + st::addContactDelta : 0) + _aboutHeight + (_cloudPwd ? st::addContactDelta + _recoverEmail.height() + st::usernameSkip : st::addContactPadding.bottom()) + _saveButton.height());
}
}
_oldPasscode.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _oldPasscode.height());
_newPasscode.setGeometry(st::addContactPadding.left(), _oldPasscode.y() + ((turningOff || cHasPasscode()) ? (_oldPasscode.height() + st::usernameSkip) : 0), _oldPasscode.width(), _oldPasscode.height());
_reenterPasscode.setGeometry(st::addContactPadding.left(), _newPasscode.y() + _newPasscode.height() + st::addContactDelta, _newPasscode.width(), _newPasscode.height());
int32 buttonTop = _height - _cancelButton.height();
_cancelButton.move(0, buttonTop);
_saveButton.move(_width - _saveButton.width(), buttonTop);
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
_badOldTimer.setSingleShot(true);
connect(&_badOldTimer, SIGNAL(timeout()), this, SLOT(onBadOldPasscode()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_oldPasscode, SIGNAL(changed()), this, SLOT(onOldChanged()));
connect(&_newPasscode, SIGNAL(changed()), this, SLOT(onNewChanged()));
connect(&_reenterPasscode, SIGNAL(changed()), this, SLOT(onNewChanged()));
connect(&_recoverEmail, SIGNAL(changed()), this, SLOT(onEmailChanged()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
connect(&_recover, SIGNAL(clicked()), this, SLOT(onRecoverByEmail()));
}
void PasscodeBox::hideAll() {
_oldPasscode.hide();
_newPasscode.hide();
_reenterPasscode.hide();
_passwordHint.hide();
_recoverEmail.hide();
_recover.hide();
_saveButton.hide();
_cancelButton.hide();
}
void PasscodeBox::showAll() {
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : cHasPasscode();
if (_turningOff) {
_oldPasscode.show();
if (_cloudPwd && _hasRecovery) {
_recover.show();
} else {
_recover.hide();
}
_passwordHint.hide();
_newPasscode.hide();
_reenterPasscode.hide();
} else {
if (cHasPasscode()) {
if (has) {
_oldPasscode.show();
if (_cloudPwd && _hasRecovery) {
_recover.show();
} else {
_recover.hide();
}
} else {
_oldPasscode.hide();
_recover.hide();
}
_newPasscode.show();
_reenterPasscode.show();
if (_cloudPwd) {
_passwordHint.show();
} else {
_passwordHint.hide();
}
if (_cloudPwd && _curSalt.isEmpty()) {
_recoverEmail.show();
} else {
_recoverEmail.hide();
}
}
_saveButton.show();
_cancelButton.show();
}
void PasscodeBox::keyPressEvent(QKeyEvent *e) {
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : cHasPasscode();
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_oldPasscode.hasFocus()) {
if (_turningOff) {
@@ -116,7 +153,7 @@ void PasscodeBox::keyPressEvent(QKeyEvent *e) {
} else if (_newPasscode.hasFocus()) {
_reenterPasscode.setFocus();
} else if (_reenterPasscode.hasFocus()) {
if (cHasPasscode() && _oldPasscode.text().isEmpty()) {
if (has && _oldPasscode.text().isEmpty()) {
_oldPasscode.setFocus();
_oldPasscode.notaBene();
} else if (_newPasscode.text().isEmpty()) {
@@ -124,111 +161,221 @@ void PasscodeBox::keyPressEvent(QKeyEvent *e) {
_newPasscode.notaBene();
} else if (_reenterPasscode.text().isEmpty()) {
_reenterPasscode.notaBene();
} else if (!_passwordHint.isHidden()) {
_passwordHint.setFocus();
} else {
onSave();
}
} else if (_passwordHint.hasFocus()) {
if (_recoverEmail.isHidden()) {
onSave();
} else {
_recoverEmail.setFocus();
}
} else if (_recoverEmail.hasFocus()) {
onSave();
}
} else if (e->key() == Qt::Key_Escape) {
onCancel();
} else {
AbstractBox::keyPressEvent(e);
}
}
void PasscodeBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void PasscodeBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
Painter p(this);
if (paint(p)) return;
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
paintTitle(p, _boxTitle, true);
p.setPen(st::usernameColor->p);
_about.draw(p, st::addContactPadding.left(), (_turningOff ? _oldPasscode : _reenterPasscode).y() + _oldPasscode.height() + st::usernameSkip, _width - st::addContactPadding.left() - st::addContactPadding.right());
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
if (!_oldError.isEmpty()) {
p.setPen(st::setErrColor->p);
p.drawText(QRect(0, _oldPasscode.y() + _oldPasscode.height(), _width, st::usernameSkip), _oldError, style::al_center);
}
int32 w = width() - st::addContactPadding.left() - st::addContactPadding.right();
int32 abouty = (_passwordHint.isHidden() ? (_reenterPasscode.isHidden() ? _oldPasscode : _reenterPasscode).y() + st::usernameSkip : _passwordHint.y() + st::addContactDelta) + _oldPasscode.height();
p.setPen(st::usernameColor->p);
_about.draw(p, st::addContactPadding.left(), abouty, w);
if (!_newError.isEmpty()) {
p.setPen(st::setErrColor->p);
p.drawText(QRect(0, _reenterPasscode.y() + _reenterPasscode.height(), _width, st::usernameSkip), _newError, style::al_center);
}
if (!_hint.isEmpty() && _oldError.isEmpty()) {
p.setPen(st::black->p);
_hintText.drawElided(p, st::addContactPadding.left(), _oldPasscode.y() + _oldPasscode.height() + ((st::usernameSkip - st::usernameFont->height) / 2), w, 1, style::al_top);
}
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
if (!_oldError.isEmpty()) {
p.setPen(st::setErrColor->p);
p.drawText(QRect(0, _oldPasscode.y() + _oldPasscode.height(), width(), st::usernameSkip), _oldError, style::al_center);
}
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, _boxTitle);
if (!_newError.isEmpty()) {
p.setPen(st::setErrColor->p);
p.drawText(QRect(0, _reenterPasscode.y() + _reenterPasscode.height(), width(), st::usernameSkip), _newError, style::al_center);
}
if (!_emailError.isEmpty()) {
p.setPen(st::setErrColor->p);
p.drawText(QRect(0, _recoverEmail.y() + _recoverEmail.height(), width(), st::usernameSkip), _emailError, style::al_center);
}
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
void PasscodeBox::resizeEvent(QResizeEvent *e) {
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : cHasPasscode();
_oldPasscode.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _oldPasscode.height());
_newPasscode.setGeometry(st::addContactPadding.left(), _oldPasscode.y() + ((_turningOff || has) ? (_oldPasscode.height() + st::usernameSkip) : 0), _oldPasscode.width(), _oldPasscode.height());
_reenterPasscode.setGeometry(st::addContactPadding.left(), _newPasscode.y() + _newPasscode.height() + st::addContactDelta, _newPasscode.width(), _newPasscode.height());
_passwordHint.setGeometry(st::addContactPadding.left(), _reenterPasscode.y() + _reenterPasscode.height() + st::usernameSkip, _reenterPasscode.width(), _reenterPasscode.height());
_recoverEmail.setGeometry(st::addContactPadding.left(), _passwordHint.y() + _passwordHint.height() + st::addContactDelta + _aboutHeight + st::addContactDelta, _passwordHint.width(), _passwordHint.height());
if (!_recover.isHidden()) {
if (_turningOff) {
_recover.move((width() - _recover.width()) / 2, _oldPasscode.y() + _oldPasscode.height() + st::usernameSkip + _aboutHeight + ((st::usernameSkip - _recover.height()) / 2));
} else {
_recover.move((width() - _recover.width()) / 2, _passwordHint.y() + _passwordHint.height() + st::addContactDelta + _aboutHeight + ((st::usernameSkip - _recover.height()) / 2));
}
}
int32 buttonTop = height() - _cancelButton.height();
_cancelButton.move(0, buttonTop);
_saveButton.move(width() - _saveButton.width(), buttonTop);
}
void PasscodeBox::showDone() {
if (_oldPasscode.isHidden()) {
_newPasscode.setFocus();
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
_oldPasscode.setFocus();
}
}
void PasscodeBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
if (_oldPasscode.isHidden()) {
_newPasscode.setFocus();
} else {
_oldPasscode.setFocus();
}
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
void PasscodeBox::setPasswordDone(const MTPBool &result) {
_setRequest = 0;
emit reloadPassword();
ConfirmBox *box = new ConfirmBox(lang(_reenterPasscode.isHidden() ? lng_cloud_password_removed : (_oldPasscode.isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated)), true, lang(lng_about_done));
App::wnd()->showLayer(box, true);
}
void PasscodeBox::onSave() {
bool PasscodeBox::setPasswordFail(const RPCError &error) {
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
_setRequest = 0;
QString err = error.type();
if (err == "PASSWORD_HASH_INVALID") {
if (_oldPasscode.isHidden()) {
emit reloadPassword();
onClose();
} else {
onBadOldPasscode();
}
} else if (err == "NEW_PASSWORD_BAD") {
_newPasscode.setFocus();
_newPasscode.notaBene();
_newError = lang(lng_cloud_password_bad);
update();
} else if (err == "NEW_SALT_INVALID") {
emit reloadPassword();
onClose();
} else if (err == "EMAIL_INVALID") {
_emailError = lang(lng_cloud_password_bad_email);
_recoverEmail.setFocus();
_recoverEmail.notaBene();
update();
} else if (err == "EMAIL_UNCONFIRMED") {
ConfirmBox *box = new ConfirmBox(lang(lng_cloud_password_almost), true, lang(lng_about_done));
App::wnd()->showLayer(box, true);
emit reloadPassword();
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
if (_oldPasscode.isHidden()) return false;
_oldPasscode.selectAll();
_oldPasscode.setFocus();
_oldPasscode.notaBene();
_oldError = lang(lng_flood_error);
}
return true;
}
void PasscodeBox::onSave(bool force) {
if (_setRequest) return;
QString old = _oldPasscode.text(), pwd = _newPasscode.text(), conf = _reenterPasscode.text();
if (_turningOff || cHasPasscode()) {
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : cHasPasscode();
if (!_cloudPwd && (_turningOff || has)) {
if (!passcodeCanTry()) {
_oldError = lang(lng_flood_error);
_oldPasscode.setFocus();
_oldPasscode.notaBene();
update();
return;
}
if (Local::checkPasscode(old.toUtf8())) {
cSetPasscodeBadTries(0);
if (_turningOff) pwd = conf = QString();
} else {
_oldPasscode.setDisabled(true);
_newPasscode.setDisabled(true);
_reenterPasscode.setDisabled(true);
_saveButton.setDisabled(true);
_oldError = QString();
update();
_badOldTimer.start(WrongPasscodeTimeout);
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
cSetPasscodeLastTry(getms(true));
onBadOldPasscode();
return;
}
}
if (!_turningOff && pwd.isEmpty()) {
_newPasscode.setFocus();
_newPasscode.notaBene();
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
return;
}
if (pwd != conf) {
_reenterPasscode.setFocus();
_reenterPasscode.notaBene();
if (!conf.isEmpty()) {
_newError = lang(lng_passcode_differ);
_newError = lang(_cloudPwd ? lng_cloud_password_differ : lng_passcode_differ);
update();
}
} else if (!_turningOff && cHasPasscode() && old == pwd) {
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
} else if (!_turningOff && has && old == pwd) {
_newPasscode.setFocus();
_newPasscode.notaBene();
_newError = lang(lng_passcode_is_same);
_newError = lang(_cloudPwd ? lng_cloud_password_is_same : lng_passcode_is_same);
update();
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
} else if (_cloudPwd) {
QString hint = _passwordHint.text(), email = _recoverEmail.text().trimmed();
if (_cloudPwd && pwd == hint && !_passwordHint.isHidden() && !_newPasscode.isHidden()) {
_newPasscode.setFocus();
_newPasscode.notaBene();
_newError = lang(lng_cloud_password_bad);
update();
if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
return;
}
if (!_recoverEmail.isHidden() && email.isEmpty() && !force) {
_replacedBy = new ConfirmBox(lang(lng_cloud_password_about_recover));
connect(_replacedBy, SIGNAL(confirmed()), this, SLOT(onForceNoMail()));
connect(_replacedBy, SIGNAL(destroyed(QObject*)), this, SLOT(onBoxDestroyed(QObject*)));
App::wnd()->replaceLayer(_replacedBy);
} else {
QByteArray newPasswordData = pwd.isEmpty() ? QByteArray() : (_newSalt + pwd.toUtf8() + _newSalt);
QByteArray newPasswordHash = pwd.isEmpty() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
if (pwd.isEmpty()) {
hint = QString();
email = QString();
} else {
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
}
QByteArray oldPasswordData = _oldPasscode.isHidden() ? QByteArray() : (_curSalt + old.toUtf8() + _curSalt);
QByteArray oldPasswordHash = _oldPasscode.isHidden() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
if (!_oldPasscode.isHidden()) {
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
}
int32 flags = MTPDaccount_passwordInputSettings::flag_new_salt | MTPDaccount_passwordInputSettings::flag_new_password_hash | MTPDaccount_passwordInputSettings::flag_hint;
if (_oldPasscode.isHidden() || _newPasscode.isHidden()) {
flags |= MTPDaccount_passwordInputSettings::flag_email;
}
MTPaccount_PasswordInputSettings settings(MTP_account_passwordInputSettings(MTP_int(flags), MTP_string(_newSalt), MTP_string(newPasswordHash), MTP_string(hint), MTP_string(email)));
_setRequest = MTP::send(MTPaccount_UpdatePasswordSettings(MTP_string(oldPasswordHash), settings), rpcDone(&PasscodeBox::setPasswordDone), rpcFail(&PasscodeBox::setPasswordFail));
}
} else {
cSetPasscodeBadTries(0);
Local::setPasscode(pwd.toUtf8());
App::wnd()->checkAutoLock();
App::wnd()->getTitle()->showUpdateBtn();
@@ -237,14 +384,10 @@ void PasscodeBox::onSave() {
}
void PasscodeBox::onBadOldPasscode() {
_oldPasscode.setDisabled(false);
_newPasscode.setDisabled(false);
_reenterPasscode.setDisabled(false);
_saveButton.setDisabled(false);
_oldPasscode.selectAll();
_oldPasscode.setFocus();
_oldPasscode.notaBene();
_oldError = lang(lng_passcode_wrong);
_oldError = lang(_cloudPwd ? lng_cloud_password_wrong : lng_passcode_wrong);
update();
}
@@ -262,18 +405,191 @@ void PasscodeBox::onNewChanged() {
}
}
void PasscodeBox::onCancel() {
emit closed();
}
void PasscodeBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
void PasscodeBox::onEmailChanged() {
if (!_emailError.isEmpty()) {
_emailError = QString();
update();
}
a_opacity.start(0);
}
PasscodeBox::~PasscodeBox() {
void PasscodeBox::onForceNoMail() {
onSave(true);
}
void PasscodeBox::onBoxDestroyed(QObject *obj) {
if (obj == _replacedBy) {
_replacedBy = 0;
}
}
void PasscodeBox::onRecoverByEmail() {
if (_pattern.isEmpty()) {
_pattern = "-";
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PasscodeBox::recoverStarted), rpcFail(&PasscodeBox::recoverStartFail));
} else {
recover();
}
}
void PasscodeBox::onRecoverExpired() {
_pattern = QString();
}
void PasscodeBox::recover() {
if (_pattern == "-") return;
_replacedBy = new RecoverBox(_pattern);
connect(_replacedBy, SIGNAL(reloadPassword()), this, SIGNAL(reloadPassword()));
connect(_replacedBy, SIGNAL(recoveryExpired()), this, SLOT(onRecoverExpired()));
connect(_replacedBy, SIGNAL(destroyed(QObject*)), this, SLOT(onBoxDestroyed(QObject*)));
App::wnd()->replaceLayer(_replacedBy);
}
void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
_pattern = qs(result.c_auth_passwordRecovery().vemail_pattern);
recover();
}
bool PasscodeBox::recoverStartFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_pattern = QString();
onClose();
return true;
}
RecoverBox::RecoverBox(const QString &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)) {
setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + st::usernameSkip + _recoverCode.height() + st::usernameSkip + st::addContactPadding.bottom() + _saveButton.height());
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSubmit()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_recoverCode, SIGNAL(changed()), this, SLOT(onCodeChanged()));
prepare();
}
void RecoverBox::hideAll() {
_recoverCode.hide();
_saveButton.hide();
_cancelButton.hide();
}
void RecoverBox::showAll() {
_recoverCode.show();
_saveButton.show();
_cancelButton.show();
}
void RecoverBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_recoverCode.text().isEmpty()) {
_recoverCode.setFocus();
_recoverCode.notaBene();
} else {
onSubmit();
}
} else {
AbstractBox::keyPressEvent(e);
}
}
void RecoverBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_signin_recover), true);
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
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), _pattern, style::al_center);
if (!_error.isEmpty()) {
p.setPen(st::setErrColor->p);
p.drawText(QRect(0, _recoverCode.y() + _recoverCode.height(), width(), st::usernameSkip), _error, style::al_center);
}
// paint button sep
p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
}
void RecoverBox::resizeEvent(QResizeEvent *e) {
_recoverCode.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top() + st::usernameSkip, width() - st::addContactPadding.left() - st::addContactPadding.right(), _recoverCode.height());
int32 buttonTop = height() - _cancelButton.height();
_cancelButton.move(0, buttonTop);
_saveButton.move(width() - _saveButton.width(), buttonTop);
}
void RecoverBox::showDone() {
_recoverCode.setFocus();
}
void RecoverBox::onSubmit() {
if (_submitRequest) return;
QString code = _recoverCode.text().trimmed();
if (code.isEmpty()) {
_recoverCode.notaBene();
return;
}
_submitRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&RecoverBox::codeSubmitDone, true), rpcFail(&RecoverBox::codeSubmitFail));
}
void RecoverBox::onCodeChanged() {
_error = QString();
update();
}
void RecoverBox::codeSubmitDone(bool recover, const MTPauth_Authorization &result) {
_submitRequest = 0;
emit reloadPassword();
ConfirmBox *box = new ConfirmBox(lang(lng_cloud_password_removed), true, lang(lng_about_done));
App::wnd()->showLayer(box, true);
}
bool RecoverBox::codeSubmitFail(const RPCError &error) {
_submitRequest = 0;
const QString &err = error.type();
if (err == "PASSWORD_EMPTY") {
emit reloadPassword();
ConfirmBox *box = new ConfirmBox(lang(lng_cloud_password_removed), true, lang(lng_about_done));
App::wnd()->showLayer(box, true);
return true;
} else if (err == "PASSWORD_RECOVERY_NA") {
onClose();
return true;
} else if (err == "PASSWORD_RECOVERY_EXPIRED") {
emit recoveryExpired();
onClose();
return true;
} else if (err == "CODE_INVALID") {
_error = lang(lng_signin_wrong_code);
update();
_recoverCode.notaBene();
return true;
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
_error = lang(lng_flood_error);
update();
_recoverCode.notaBene();
return true;
}
if (cDebug()) { // internal server error
_error = err + ": " + error.description();
} else {
_error = lang(lng_server_error);
}
update();
_recoverCode.setFocus();
return false;
}

View File

@@ -17,48 +17,110 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class PasscodeBox : public LayeredWidget {
class PasscodeBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
PasscodeBox(bool turningOff = false);
void parentResized();
void animStep(float64 dt);
PasscodeBox(const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff = false);
void init();
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~PasscodeBox();
void resizeEvent(QResizeEvent *e);
public slots:
void onSave();
void onSave(bool force = false);
void onBadOldPasscode();
void onOldChanged();
void onNewChanged();
void onCancel();
void onEmailChanged();
void onForceNoMail();
void onBoxDestroyed(QObject *obj);
void onRecoverByEmail();
void onRecoverExpired();
private:
signals:
void reloadPassword();
protected:
void hideAll();
void showAll();
void showDone();
bool _turningOff;
private:
void setPasswordDone(const MTPBool &result);
bool setPasswordFail(const RPCError &error);
void recoverStarted(const MTPauth_PasswordRecovery &result);
bool recoverStartFail(const RPCError &error);
void recover();
QString _pattern;
AbstractBox *_replacedBy;
bool _turningOff, _cloudPwd;
mtpRequestId _setRequest;
QByteArray _newSalt, _curSalt;
bool _hasRecovery;
QString _hint;
int32 _aboutHeight;
QString _boxTitle;
Text _about;
Text _about, _hintText;
int32 _width, _height;
FlatButton _saveButton, _cancelButton;
FlatInput _oldPasscode, _newPasscode, _reenterPasscode;
FlatInput _oldPasscode, _newPasscode, _reenterPasscode, _passwordHint, _recoverEmail;
LinkButton _recover;
QPixmap _cache;
anim::fvalue a_opacity;
bool _hiding;
QTimer _badOldTimer;
QString _oldError, _newError;
QString _oldError, _newError, _emailError;
};
class RecoverBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
RecoverBox(const QString &pattern);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
public slots:
void onSubmit();
void onCodeChanged();
signals:
void reloadPassword();
void recoveryExpired();
protected:
void hideAll();
void showAll();
void showDone();
private:
void codeSubmitDone(bool recover, const MTPauth_Authorization &result);
bool codeSubmitFail(const RPCError &error);
mtpRequestId _submitRequest;
QString _pattern;
FlatButton _saveButton, _cancelButton;
FlatInput _recoverCode;
QString _error;
};

View File

@@ -27,10 +27,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : _downState(0),
_sendButton(this, lang(lng_settings_save), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_img(img), _peerId(peer), a_opacity(0, 1) {
_img(img), _peerId(peer) {
connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
if (_peerId) {
connect(this, SIGNAL(ready(const QImage &)), this, SLOT(onReady(const QImage &)));
}
@@ -46,13 +46,12 @@ PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : _downState(0
}
_cropx = (_thumbw - _cropw) / 2;
_cropy = (_thumbh - _cropw) / 2;
_width = st::cropBoxWidth;
_height = _thumbh + st::boxPadding.top() + st::boxFont->height + st::boxPadding.top() + st::boxPadding.bottom() + _sendButton.height();
_thumbx = (_width - _thumbw) / 2;
_thumbx = (st::cropBoxWidth - _thumbw) / 2;
_thumby = st::boxPadding.top() * 2 + st::boxFont->height;
setMouseTracking(true);
resize(_width, _height);
resizeMaxHeight(st::cropBoxWidth, _thumbh + st::boxPadding.top() + st::boxFont->height + st::boxPadding.top() + st::boxPadding.bottom() + _sendButton.height());
}
void PhotoCropBox::mousePressEvent(QMouseEvent *e) {
@@ -195,39 +194,26 @@ void PhotoCropBox::mouseMoveEvent(QMouseEvent *e) {
void PhotoCropBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
onSend();
} else if (e->key() == Qt::Key_Escape) {
onCancel();
} else {
AbstractBox::keyPressEvent(e);
}
}
void PhotoCropBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
_sendButton.move(_width - _sendButton.width(), _height - _sendButton.height());
_cancelButton.move(0, _height - _cancelButton.height());
update();
}
void PhotoCropBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
p.setOpacity(a_opacity.current());
if (paint(p)) return;
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
// paint shadows
p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
p.setFont(st::boxFont->f);
p.setPen(st::boxGrayTitle->p);
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), _width - st::boxPadding.left() - st::boxPadding.right(), st::boxFont->height), lang(lng_settings_crop_profile), style::al_center);
paintGrayTitle(p, lang(lng_settings_crop_profile));
p.translate(_thumbx, _thumby);
p.drawPixmap(0, 0, _thumb);
p.setOpacity(a_opacity.current() * 0.5);
p.setOpacity(0.5);
if (_cropy > 0) {
p.fillRect(QRect(0, 0, _cropx + _cropw, _cropy), st::black->b);
}
@@ -248,15 +234,9 @@ void PhotoCropBox::paintEvent(QPaintEvent *e) {
p.fillRect(QRect(_cropx + mdelta, _cropy + _cropw + mdelta, delta, delta), st::white->b);
}
void PhotoCropBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
} else {
a_opacity.update(ms, anim::linear);
}
_sendButton.setOpacity(a_opacity.current());
_cancelButton.setOpacity(a_opacity.current());
update();
void PhotoCropBox::resizeEvent(QResizeEvent *e) {
_sendButton.move(width() - _sendButton.width(), height() - _sendButton.height());
_cancelButton.move(0, height() - _cancelButton.height());
}
void PhotoCropBox::onSend() {
@@ -296,14 +276,12 @@ void PhotoCropBox::onReady(const QImage &tosend) {
emit closed();
}
void PhotoCropBox::onCancel() {
emit closed();
void PhotoCropBox::hideAll() {
_sendButton.hide();
_cancelButton.hide();
}
void PhotoCropBox::startHide() {
_hiding = true;
a_opacity.start(0);
}
PhotoCropBox::~PhotoCropBox() {
void PhotoCropBox::showAll() {
_sendButton.show();
_cancelButton.show();
}

View File

@@ -17,39 +17,41 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class PhotoCropBox : public LayeredWidget {
class PhotoCropBox : public AbstractBox {
Q_OBJECT
public:
PhotoCropBox(const QImage &img, const PeerId &peer);
void parentResized();
void animStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
void resizeEvent(QResizeEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
int32 mouseState(QPoint p);
~PhotoCropBox();
public slots:
void onSend();
void onCancel();
void onReady(const QImage &tosend);
signals:
void ready(const QImage &tosend);
protected:
void hideAll();
void showAll();
private:
int32 _downState;
int32 _width, _height, _thumbx, _thumby, _thumbw, _thumbh;
int32 _thumbx, _thumby, _thumbw, _thumbh;
int32 _cropx, _cropy, _cropw;
int32 _fromposx, _fromposy, _fromcropx, _fromcropy, _fromcropw;
FlatButton _sendButton, _cancelButton;
@@ -57,8 +59,4 @@ private:
QPixmap _thumb;
PeerId _peerId;
anim::fvalue a_opacity;
bool _hiding;
};

View File

@@ -29,11 +29,10 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(new ReadyLocalMedi
_compressed(this, lang(lng_send_image_compressed), cCompressPastedImage()),
_sendButton(this, lang(lng_send_button), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
a_opacity(0, 1) {
_replyTo(img.replyTo) {
connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
_width = st::confirmWidth;
if (_img->type == ToPreparePhoto) {
int32 maxW = 0, maxH = 0;
for (PreparedPhotoThumbs::const_iterator i = _img->photoThumbs.cbegin(), e = _img->photoThumbs.cend(); i != e; ++i) {
@@ -47,7 +46,7 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(new ReadyLocalMedi
if (!tw || !th) {
tw = th = 1;
}
_thumbw = _width - st::boxPadding.left() - st::boxPadding.right();
_thumbw = width() - st::boxPadding.left() - st::boxPadding.right();
if (_thumb.width() < _thumbw) {
_thumbw = (_thumb.width() > 20) ? _thumb.width() : 20;
}
@@ -60,7 +59,7 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(new ReadyLocalMedi
_thumbw = 10;
}
}
_height = _thumbh + st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::boxPadding.bottom() + _compressed.height() + _sendButton.height();
resizeMaxHeight(st::boxWidth, _thumbh + st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::boxPadding.bottom() + _compressed.height() + _sendButton.height());
_thumb = QPixmap::fromImage(_thumb.toImage().scaled(_thumbw, _thumbh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
} else {
@@ -84,86 +83,69 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(new ReadyLocalMedi
_thumb = QPixmap::fromImage(_thumb.toImage().scaledToWidth(_thumbw * cIntRetinaFactor(), Qt::SmoothTransformation), Qt::ColorOnly);
_thumb.setDevicePixelRatio(cRetinaFactor());
}
_height = st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom() + st::boxPadding.bottom() + _sendButton.height();
resizeMaxHeight(st::boxWidth, st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom() + st::boxPadding.bottom() + _sendButton.height());
_name = _img->filename;
_namew = st::mediaFont->m.width(_name);
_size = formatSizeText(_img->filesize);
_textw = qMax(_namew, st::mediaFont->m.width(_size));
}
resize(_width, _height);
prepare();
}
PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QString &lname) : _img(0),
PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo) : _img(0),
_thumbx(0), _thumby(0), _thumbw(0), _thumbh(0), _namew(0), _textw(0),
_compressed(this, lang(lng_send_image_compressed), true),
_sendButton(this, lang(lng_send_button), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
_phone(phone), _fname(fname), _lname(lname),
a_opacity(0, 1) {
_phone(phone), _fname(fname), _lname(lname), _replyTo(replyTo) {
connect(&_sendButton, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
_width = st::confirmWidth;
_compressed.hide();
_height = st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom() + st::boxPadding.bottom() + _sendButton.height();
_name = _fname + QChar(' ') + _lname;
_namew = st::mediaFont->m.width(_name);
_size = _phone;
_textw = qMax(_namew, st::mediaFont->m.width(_size));
resize(_width, _height);
resizeMaxHeight(st::boxWidth, st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom() + st::boxPadding.bottom() + _sendButton.height());
prepare();
}
void PhotoSendBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
onSend((e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier)) && e->modifiers().testFlag(Qt::ShiftModifier));
} else if (e->key() == Qt::Key_Escape) {
onCancel();
} else {
AbstractBox::keyPressEvent(e);
}
}
void PhotoSendBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
_sendButton.move(_width - _sendButton.width(), _height - _sendButton.height());
_cancelButton.move(0, _height - _cancelButton.height());
_compressed.move((width() - _compressed.width()) / 2, _height - _cancelButton.height() - _compressed.height() - st::confirmCompressedSkip);
update();
}
void PhotoSendBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
p.setOpacity(a_opacity.current());
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
if (paint(p)) return;
// paint shadows
p.fillRect(0, _height - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// paint button sep
p.fillRect(st::btnSelectCancel.width, _height - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
p.fillRect(st::btnSelectCancel.width, height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b);
p.setFont(st::boxFont->f);
p.setPen(st::boxGrayTitle->p);
if (_img && _img->type == ToPreparePhoto) {
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), _width - st::boxPadding.left() - st::boxPadding.right(), st::boxFont->height), lang(lng_really_send_image), style::al_center);
p.drawPixmap((_width - _thumbw) / 2, st::boxPadding.top() * 2 + st::boxFont->height, _thumb);
paintGrayTitle(p, lang(lng_really_send_image));
p.drawPixmap((width() - _thumbw) / 2, st::boxPadding.top() * 2 + st::boxFont->height, _thumb);
} else {
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), _width - st::boxPadding.left() - st::boxPadding.right(), st::boxFont->height), lang(_img ? lng_really_send_file : lng_really_share_contact), style::al_center);
paintGrayTitle(p, lang(_img ? lng_really_send_file : lng_really_share_contact));
int32 w = _width - st::boxPadding.left() - st::boxPadding.right(), h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
int32 w = width() - st::boxPadding.left() - st::boxPadding.right(), h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
int32 twidth = w - tleft - st::mediaPadding.right();
if (twidth > _textw) {
w -= (twidth - _textw);
twidth = _textw;
}
int32 x = (_width - w) / 2, y = st::boxPadding.top() * 2 + st::boxFont->height;
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);
@@ -189,21 +171,31 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) {
}
}
void PhotoSendBox::animStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
} else {
a_opacity.update(ms, anim::linear);
}
_sendButton.setOpacity(a_opacity.current());
_cancelButton.setOpacity(a_opacity.current());
_compressed.setOpacity(a_opacity.current());
update();
void PhotoSendBox::resizeEvent(QResizeEvent *e) {
_sendButton.move(width() - _sendButton.width(), height() - _sendButton.height());
_cancelButton.move(0, height() - _cancelButton.height());
_compressed.move((width() - _compressed.width()) / 2, height() - _cancelButton.height() - _compressed.height() - st::confirmCompressedSkip);
}
void PhotoSendBox::closePressed() {
if (App::main()) App::main()->cancelSendImage();
}
void PhotoSendBox::hideAll() {
_sendButton.hide();
_cancelButton.hide();
_compressed.hide();
}
void PhotoSendBox::showAll() {
_sendButton.show();
_cancelButton.show();
_compressed.show();
}
void PhotoSendBox::onSend(bool ctrlShiftEnter) {
if (!_img) {
if (App::main()) App::main()->confirmShareContact(ctrlShiftEnter, _phone, _fname, _lname);
if (App::main()) App::main()->confirmShareContact(ctrlShiftEnter, _phone, _fname, _lname, _replyTo);
} else {
if (!_compressed.isHidden()) {
if (_compressed.checked() != cCompressPastedImage()) {
@@ -215,23 +207,14 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) {
_img->ctrlShiftEnter = ctrlShiftEnter;
if (App::main()) App::main()->confirmSendImage(*_img);
} else {
if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter);
if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter, _replyTo);
}
}
emit confirmed();
emit closed();
}
void PhotoSendBox::onCancel() {
if (App::main()) App::main()->cancelSendImage();
emit closed();
}
void PhotoSendBox::startHide() {
_hiding = true;
a_opacity.start(0);
}
PhotoSendBox::~PhotoSendBox() {
delete _img;
if (App::main()) App::main()->cancelSendImage();
closePressed();
}

View File

@@ -17,32 +17,39 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
#include "localimageloader.h"
class PhotoSendBox : public LayeredWidget {
class PhotoSendBox : public AbstractBox {
Q_OBJECT
public:
PhotoSendBox(const ReadyLocalMedia &img);
PhotoSendBox(const QString &phone, const QString &fname, const QString &lname);
void parentResized();
void animStep(float64 ms);
PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
void resizeEvent(QResizeEvent *e);
~PhotoSendBox();
signals:
void confirmed();
public slots:
void onSend(bool ctrlShiftEnter = false);
void onCancel();
protected:
void closePressed();
void hideAll();
void showAll();
private:
ReadyLocalMedia *_img;
int32 _width, _height, _thumbx, _thumby, _thumbw, _thumbh;
int32 _thumbx, _thumby, _thumbw, _thumbh;
QString _name, _size;
int32 _namew, _textw;
FlatCheckbox _compressed;
@@ -50,9 +57,7 @@ private:
QPixmap _thumb;
QString _phone, _fname, _lname;
MsgId _replyTo;
anim::fvalue a_opacity;
bool _hiding;
};

View File

@@ -0,0 +1,428 @@
/*
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 "localstorage.h"
#include "sessionsbox.h"
#include "mainwidget.h"
#include "window.h"
#include "countries.h"
#include "confirmbox.h"
SessionsInner::SessionsInner(SessionsList *list) : _list(list), _terminating(0), _terminateBox(0) {
}
void SessionsInner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
p.fillRect(r, st::white->b);
p.setFont(st::linkFont->f);
int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip;
int32 w = width() - 2 * x, availw = width() - 2 * xact;
int32 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;
if (to > count) to = count;
p.translate(0, from * st::sessionHeight);
for (int32 i = from; i < to; ++i) {
const SessionData &auth(_list->at(i));
p.setFont(st::sessionNameFont->f);
p.setPen(st::black->p);
p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth);
p.setFont(st::sessionActiveFont->f);
p.setPen(st::sessionActiveColor->p);
p.drawTextRight(xact, st::sessionPadding.top(), availw, auth.active, auth.activeWidth);
p.setFont(st::sessionInfoFont->f);
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);
}
}
}
void SessionsInner::onTerminate() {
for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) {
if (i.value()->getState() & Button::StateOver) {
_terminating = i.key();
if (_terminateBox) _terminateBox->deleteLater();
_terminateBox = new ConfirmBox(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button));
connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateSure()));
connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*)));
App::wnd()->replaceLayer(_terminateBox);
}
}
}
void SessionsInner::onTerminateSure() {
if (_terminateBox) {
_terminateBox->onClose();
_terminateBox = 0;
}
MTP::send(MTPaccount_ResetAuthorization(MTP_long(_terminating)), rpcDone(&SessionsInner::terminateDone, _terminating), rpcFail(&SessionsInner::terminateFail, _terminating));
TerminateButtons::iterator i = _terminateButtons.find(_terminating);
if (i != _terminateButtons.cend()) {
i.value()->clearState();
i.value()->hide();
}
}
void SessionsInner::onNoTerminateBox(QObject *obj) {
if (obj == _terminateBox) _terminateBox = 0;
}
void SessionsInner::terminateDone(uint64 hash, const MTPBool &result) {
for (int32 i = 0, l = _list->size(); i < l; ++i) {
if (_list->at(i).hash == hash) {
_list->removeAt(i);
break;
}
}
listUpdated();
emit oneTerminated();
}
bool SessionsInner::terminateFail(uint64 hash, const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
TerminateButtons::iterator i = _terminateButtons.find(hash);
if (i != _terminateButtons.end()) {
i.value()->show();
return true;
}
return false;
}
void SessionsInner::resizeEvent(QResizeEvent *e) {
}
void SessionsInner::listUpdated() {
for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) {
i.value()->move(0, -1);
}
for (int32 i = 0, l = _list->size(); i < l; ++i) {
TerminateButtons::iterator j = _terminateButtons.find(_list->at(i).hash);
if (j == _terminateButtons.cend()) {
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);
}
for (TerminateButtons::iterator i = _terminateButtons.begin(); i != _terminateButtons.cend();) {
if (i.value()->y() >= 0) {
++i;
} else {
delete i.value();
i = _terminateButtons.erase(i);
}
}
resize(width(), _list->isEmpty() ? st::noContactsHeight : (_list->size() * st::sessionHeight));
if (parentWidget()) parentWidget()->update();
}
SessionsInner::~SessionsInner() {
for (int32 i = 0, l = _terminateButtons.size(); i < l; ++i) {
delete _terminateButtons[i];
}
}
SessionsBox::SessionsBox() : ScrollableBox(st::boxScroll), _loading(true), _inner(&_list),
_done(this, lang(lng_about_done), st::sessionsCloseButton),
_terminateAll(this, lang(lng_sessions_terminate_all)), _terminateBox(0), _shortPollRequest(0) {
setMaxHeight(st::sessionsHeight);
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_terminateAll, SIGNAL(clicked()), this, SLOT(onTerminateAll()));
connect(&_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated()));
connect(App::wnd(), SIGNAL(newAuthorization()), this, SLOT(onNewAuthorization()));
connect(&_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations()));
init(&_inner, _done.height(), st::boxTitleHeight + st::sessionHeight + st::boxTitleHeight);
_inner.resize(width(), st::noContactsHeight);
prepare();
_scroll.hide();
MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
}
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());
}
void SessionsBox::hideAll() {
_done.hide();
_terminateAll.hide();
ScrollableBox::hideAll();
}
void SessionsBox::showAll() {
_done.show();
if (_list.isEmpty()) {
_terminateAll.hide();
_scroll.hide();
} else {
_terminateAll.show();
if (_loading) {
_scroll.hide();
} else {
_scroll.show();
}
}
}
void SessionsBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_sessions_header), true);
p.translate(0, st::boxTitleHeight);
if (_loading) {
p.setFont(st::noContactsFont->f);
p.setPen(st::noContactsColor->p);
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();
p.setFont(st::sessionNameFont->f);
p.setPen(st::black->p);
p.drawTextLeft(x, st::sessionPadding.top(), w, _current.name, _current.nameWidth);
p.setFont(st::sessionActiveFont->f);
p.setPen(st::sessionActiveColor->p);
p.drawTextRight(x, st::sessionPadding.top(), w, _current.active, _current.activeWidth);
p.setFont(st::sessionInfoFont->f);
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);
p.setFont(st::sessionInfoFont->f);
p.setPen(st::sessionInfoColor->p);
p.drawText(QRect(st::sessionPadding.left(), st::boxTitleHeight + st::boxTitlePos.y(), width() - st::sessionPadding.left() - st::sessionPadding.right(), _scroll.height()), lang(lng_sessions_other_desc), style::al_topleft);
// paint shadow
p.fillRect(0, height() - st::sessionsCloseButton.height - st::scrollDef.bottomsh - st::sessionHeight - st::boxTitleHeight, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
} else {
paintTitle(p, lang(lng_sessions_other_header), false);
}
}
}
void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
_loading = false;
_shortPollRequest = 0;
int32 availCurrent = st::boxWidth - st::sessionPadding.left() - 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);
int32 l = v.size();
if (l > 1) _list.reserve(l - 1);
const CountriesByISO2 &countries(countriesByISO2());
for (int32 i = 0; i < l; ++i) {
const MTPDauthorization &d(v.at(i).c_authorization());
SessionData data;
data.hash = d.vhash.v;
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), 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 = 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;
if (data.nameWidth > availCurrent) {
data.name = st::sessionNameFont->m.elidedText(data.name, Qt::ElideRight, availCurrent);
data.nameWidth = st::sessionNameFont->m.width(data.name);
}
data.infoWidth = st::sessionInfoFont->m.width(data.info);
if (data.infoWidth > availCurrent) {
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));
QDate nowDate(now.date()), lastDate(lastTime.date());
QString dt;
if (lastDate == nowDate) {
data.active = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) {
data.active = langDayOfWeek(lastDate);
} else {
data.active = lastDate.toString(qsl("d.MM.yy"));
}
data.activeWidth = st::sessionActiveFont->m.width(data.active);
int32 availForName = availOther - st::sessionPadding.right() - data.activeWidth;
if (data.nameWidth > availForName) {
data.name = st::sessionNameFont->m.elidedText(data.name, Qt::ElideRight, availForName);
data.nameWidth = st::sessionNameFont->m.width(data.name);
}
data.infoWidth = st::sessionInfoFont->m.width(data.info);
if (data.infoWidth > availOther) {
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;) {
--i;
if (_list.at(i).activeTime > _list.at(i - 1).activeTime) {
qSwap(_list[i], _list[i - 1]);
}
}
}
}
_inner.listUpdated();
if (!_done.isHidden()) {
showAll();
update();
}
_shortPollTimer.start(SessionsShortPollTimeout);
}
void SessionsBox::onTerminateAll() {
if (_terminateBox) _terminateBox->deleteLater();
_terminateBox = new ConfirmBox(lang(lng_settings_reset_sure), lang(lng_settings_reset_button));
connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateAllSure()));
connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*)));
App::wnd()->replaceLayer(_terminateBox);
}
void SessionsBox::onTerminateAllSure() {
if (_terminateBox) {
_terminateBox->onClose();
_terminateBox = 0;
}
MTP::send(MTPauth_ResetAuthorizations(), rpcDone(&SessionsBox::terminateAllDone), rpcFail(&SessionsBox::terminateAllFail));
_loading = true;
if (!_done.isHidden()) {
showAll();
update();
}
}
void SessionsBox::onNoTerminateBox(QObject *obj) {
if (obj == _terminateBox) _terminateBox = 0;
}
void SessionsBox::onOneTerminated() {
if (_list.isEmpty()) {
if (!_done.isHidden()) {
showAll();
update();
}
}
}
void SessionsBox::onShortPollAuthorizations() {
if (!_shortPollRequest) {
_shortPollRequest = MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
if (!_done.isHidden()) {
showAll();
update();
}
}
}
void SessionsBox::onNewAuthorization() {
onShortPollAuthorizations();
// _shortPollTimer.start(1000);
}
void SessionsBox::terminateAllDone(const MTPBool &result) {
MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
if (_shortPollRequest) {
MTP::cancel(_shortPollRequest);
_shortPollRequest = 0;
}
}
bool SessionsBox::terminateAllFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
if (_shortPollRequest) {
MTP::cancel(_shortPollRequest);
_shortPollRequest = 0;
}
return true;
}

View File

@@ -0,0 +1,115 @@
/*
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 ConfirmBox;
struct SessionData {
uint64 hash;
int32 activeTime;
int32 nameWidth, activeWidth, infoWidth, ipWidth;
QString name, active, info, ip;
};
typedef QList<SessionData> SessionsList;
class SessionsInner : public QWidget, public RPCSender {
Q_OBJECT
public:
SessionsInner(SessionsList *list);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void listUpdated();
~SessionsInner();
signals:
void oneTerminated();
public slots:
void onTerminate();
void onTerminateSure();
void onNoTerminateBox(QObject *obj);
private:
void terminateDone(uint64 hash, const MTPBool &result);
bool terminateFail(uint64 hash, const RPCError &error);
SessionsList *_list;
typedef QMap<uint64, IconedButton*> TerminateButtons;
TerminateButtons _terminateButtons;
uint64 _terminating;
ConfirmBox *_terminateBox;
};
class SessionsBox : public ScrollableBox, public RPCSender {
Q_OBJECT
public:
SessionsBox();
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
public slots:
void onTerminateAll();
void onTerminateAllSure();
void onNoTerminateBox(QObject *obj);
void onOneTerminated();
void onShortPollAuthorizations();
void onNewAuthorization();
protected:
void hideAll();
void showAll();
private:
void gotAuthorizations(const MTPaccount_Authorizations &result);
void terminateAllDone(const MTPBool &res);
bool terminateAllFail(const RPCError &error);
bool _loading;
SessionData _current;
SessionsList _list;
SessionsInner _inner;
FlatButton _done;
LinkButton _terminateAll;
ConfirmBox *_terminateBox;
SingleTimer _shortPollTimer;
mtpRequestId _shortPollRequest;
};

View File

@@ -55,34 +55,23 @@ UsernameBox::UsernameBox() :
_saveButton(this, lang(lng_settings_save), st::usernameDone),
_cancelButton(this, lang(lng_cancel), st::usernameCancel),
_usernameInput(this, st::inpAddContact, qsl("@username"), App::self()->username),
_saveRequest(0), _checkRequest(0), _about(st::usernameWidth - 2 * st::addContactTitlePos.x()),
a_opacity(0, 1), _hiding(false) {
_saveRequest(0), _checkRequest(0), _about(st::usernameWidth - 2 * st::boxTitlePos.x()) {
_about.setRichText(st::usernameFont, lang(lng_username_about));
_goodText = App::self()->username.isEmpty() ? QString() : lang(lng_username_available);
initBox();
}
void UsernameBox::initBox() {
_width = st::usernameWidth;
_height = st::addContactTitleHeight + st::addContactPadding.top() + _usernameInput.height() + st::addContactPadding.bottom() + _about.countHeight(st::usernameWidth - 2 * st::addContactTitlePos.x()) + st::usernameSkip + _saveButton.height();
_usernameInput.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _usernameInput.height());
int32 buttonTop = _height - _cancelButton.height();
_cancelButton.move(0, buttonTop);
_saveButton.move(_width - _saveButton.width(), buttonTop);
resizeMaxHeight(st::usernameWidth, st::boxTitleHeight + st::addContactPadding.top() + _usernameInput.height() + st::addContactPadding.bottom() + _about.countHeight(st::usernameWidth - 2 * st::boxTitlePos.x()) + st::usernameSkip + _saveButton.height());
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_usernameInput, SIGNAL(changed()), this, SLOT(onChanged()));
_checkTimer.setSingleShot(true);
connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck()));
resize(_width, _height);
showAll();
_cache = myGrab(this, rect());
hideAll();
prepare();
}
void UsernameBox::hideAll() {
@@ -97,71 +86,51 @@ void UsernameBox::showAll() {
_cancelButton.show();
}
void UsernameBox::showDone() {
_usernameInput.setFocus();
}
void UsernameBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
onSave();
} else if (e->key() == Qt::Key_Escape) {
onCancel();
} else {
AbstractBox::keyPressEvent(e);
}
}
void UsernameBox::parentResized() {
QSize s = parentWidget()->size();
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
update();
}
void UsernameBox::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_cache.isNull()) {
if (!_hiding || a_opacity.current() > 0.01) {
// fill bg
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
Painter p(this);
if (paint(p)) return;
// paint shadows
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
p.fillRect(0, size().height() - st::usernameCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
paintTitle(p, lang(lng_username_title), true);
// paint button sep
p.fillRect(st::usernameCancel.width, size().height() - st::usernameCancel.height, st::lineWidth, st::usernameCancel.height, st::btnSelectSep->b);
// paint shadow
p.fillRect(0, height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, width(), st::scrollDef.bottomsh, st::scrollDef.shColor->b);
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_username_title));
// paint button sep
p.fillRect(st::usernameCancel.width, size().height() - st::usernameCancel.height, st::lineWidth, st::usernameCancel.height, st::btnSelectSep->b);
if (!_errorText.isEmpty()) {
p.setPen(st::setErrColor->p);
p.setFont(st::setErrFont->f);
int32 w = st::setErrFont->m.width(_errorText);
p.drawText((_width - w) / 2, _usernameInput.y() + _usernameInput.height() + ((st::usernameSkip - st::setErrFont->height) / 2) + st::setErrFont->ascent, _errorText);
} else if (!_goodText.isEmpty()) {
p.setPen(st::setGoodColor->p);
p.setFont(st::setErrFont->f);
int32 w = st::setErrFont->m.width(_goodText);
p.drawText((_width - w) / 2, _usernameInput.y() + _usernameInput.height() + ((st::usernameSkip - st::setErrFont->height) / 2) + st::setErrFont->ascent, _goodText);
}
p.setPen(st::usernameColor->p);
_about.draw(p, st::addContactTitlePos.x(), _usernameInput.y() + _usernameInput.height() + st::usernameSkip, width() - 2 * st::addContactTitlePos.x());
}
} else {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
if (!_errorText.isEmpty()) {
p.setPen(st::setErrColor->p);
p.setFont(st::setErrFont->f);
int32 w = st::setErrFont->m.width(_errorText);
p.drawText((width() - w) / 2, _usernameInput.y() + _usernameInput.height() + ((st::usernameSkip - st::setErrFont->height) / 2) + st::setErrFont->ascent, _errorText);
} else if (!_goodText.isEmpty()) {
p.setPen(st::setGoodColor->p);
p.setFont(st::setErrFont->f);
int32 w = st::setErrFont->m.width(_goodText);
p.drawText((width() - w) / 2, _usernameInput.y() + _usernameInput.height() + ((st::usernameSkip - st::setErrFont->height) / 2) + st::setErrFont->ascent, _goodText);
}
p.setPen(st::usernameColor->p);
_about.draw(p, st::boxTitlePos.x(), _usernameInput.y() + _usernameInput.height() + st::usernameSkip, width() - 2 * st::boxTitlePos.x());
}
void UsernameBox::animStep(float64 dt) {
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (!_hiding) {
showAll();
_usernameInput.setFocus();
}
} else {
a_opacity.update(dt, anim::linear);
}
update();
void UsernameBox::resizeEvent(QResizeEvent *e) {
_usernameInput.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _usernameInput.height());
int32 buttonTop = height() - _cancelButton.height();
_cancelButton.move(0, buttonTop);
_saveButton.move(width() - _saveButton.width(), buttonTop);
}
void UsernameBox::onSave() {
@@ -225,6 +194,8 @@ void UsernameBox::onUpdateDone(const MTPUser &user) {
}
bool UsernameBox::onUpdateFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_saveRequest = 0;
QString err(error.type()), name = getName();
if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == App::self()->username) {
@@ -258,6 +229,8 @@ void UsernameBox::onCheckDone(const MTPBool &result) {
}
bool UsernameBox::onCheckFail(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_checkRequest = 0;
QString err(error.type());
if (err == "USERNAME_INVALID") {
@@ -277,19 +250,3 @@ bool UsernameBox::onCheckFail(const RPCError &error) {
QString UsernameBox::getName() const {
return _usernameInput.text().replace('@', QString()).trimmed();
}
void UsernameBox::onCancel() {
emit closed();
}
void UsernameBox::startHide() {
_hiding = true;
if (_cache.isNull()) {
_cache = myGrab(this, rect());
hideAll();
}
a_opacity.start(0);
}
UsernameBox::~UsernameBox() {
}

View File

@@ -17,7 +17,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layerwidget.h"
#include "abstractbox.h"
class UsernameInput : public FlatInput {
public:
@@ -30,31 +30,30 @@ protected:
};
class UsernameBox : public LayeredWidget, public RPCSender {
class UsernameBox : public AbstractBox, public RPCSender {
Q_OBJECT
public:
UsernameBox();
void parentResized();
void animStep(float64 dt);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void startHide();
~UsernameBox();
void resizeEvent(QResizeEvent *e);
public slots:
void onSave();
void onCancel();
void onCheck();
void onChanged();
private:
protected:
void hideAll();
void showAll();
void showDone();
private:
void onUpdateDone(const MTPUser &result);
bool onUpdateFail(const RPCError &error);
@@ -65,18 +64,12 @@ private:
QString getName() const;
void initBox();
int32 _width, _height;
FlatButton _saveButton, _cancelButton;
UsernameInput _usernameInput;
QPixmap _cache;
mtpRequestId _saveRequest, _checkRequest;
QString _sentUsername, _checkUsername, _errorText, _goodText;
Text _about;
QTimer _checkTimer;
anim::fvalue a_opacity;
bool _hiding;
};

View File

@@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 7020;
static const wchar_t *AppVersionStr = L"0.7.20";
static const bool DevChannel = false;
static const int32 AppVersion = 8002;
static const wchar_t *AppVersionStr = L"0.8.2";
static const bool DevChannel = true;
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";
@@ -118,9 +118,10 @@ enum {
SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text
SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs
HiddenIsOnlineAfterMessage = 30, // user with hidden last seen stays online for such amount of seconds in the interface
HiddenIsOnlineAfterMessage = 30, // user with hidden last seen stays online for such amount of seconds in the interface
ServiceUserId = 777000,
WebPageUserId = 701000,
CacheBackgroundTimeout = 3000, // cache background scaled image after 3s
BackgroundsInRow = 3,
@@ -129,6 +130,7 @@ enum {
UpdateDelayRandPart = 8 * 3600, // 8 hour max - min time between update check requests
WrongPasscodeTimeout = 1500,
SessionsShortPollTimeout = 60000,
};
inline bool isServiceUser(uint64 id) {
@@ -216,18 +218,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() {
@@ -238,7 +242,7 @@ static const char *ApiLang = "en";
extern QString gKeyFile;
inline const QString &cDataFile() {
if (!gKeyFile.isEmpty()) return gKeyFile;
static const QString res(cTestMode() ? qsl("data_test") : qsl("data"));
static const QString res(qsl("data"));
return res;
}
@@ -277,7 +281,7 @@ enum {
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
WaitForSeqTimeout = 1000, // 1s wait for skipped seq in updates
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
NotifyWindowsCount = 3, // 3 desktop notifies at the same time
@@ -285,7 +289,7 @@ enum {
UpdateChunk = 100 * 1024, // 100kb parts when downloading the update
IdleMsecs = 60 * 1000, // after 60secs without user input we think we are idle
ForwardOnAdd = 120, // how many messages from chat history server should forward to user, that was added to this chat
ForwardOnAdd = 100, // how many messages from chat history server should forward to user, that was added to this chat
};
inline const QRegularExpression &cWordSplit() {

View File

@@ -15,6 +15,15 @@ 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
struct CountryInfo {
CountryInfo(const char *_name, const char *_iso2, const char *_code) : name(_name), iso2(_iso2), code(_code) {
}
const char *name, *iso2, *code;
};
static const CountryInfo countries[] = {
CountryInfo("Afghanistan", "AF", "93"),
CountryInfo("Albania", "AL", "355"),
@@ -248,3 +257,9 @@ static const CountryInfo countries[] = {
CountryInfo("Zambia", "ZM", "260"),
CountryInfo("Zimbabwe", "ZW", "263"),
};
typedef QHash<QString, const CountryInfo *> CountriesByCode;
typedef QHash<QString, const CountryInfo *> CountriesByISO2;
const CountriesByCode &countriesByCode();
const CountriesByISO2 &countriesByISO2();

View File

@@ -23,7 +23,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "dialogswidget.h"
#include "mainwidget.h"
#include "boxes/addcontactbox.h"
#include "boxes/newgroupbox.h"
#include "boxes/contactsbox.h"
#include "localstorage.h"
DialogsListWidget::DialogsListWidget(QWidget *parent, MainWidget *main) : QWidget(parent),
dialogs(false),
@@ -32,13 +34,15 @@ contacts(true),
sel(0),
contactSel(false),
selByMouse(false),
hashtagSel(-1),
filteredSel(-1),
searchedCount(0),
searchedSel(-1),
peopleSel(-1),
_lastSearchId(0),
_state(DefaultState),
_addContactLnk(this, lang(lng_add_contact_button)) {
_addContactLnk(this, lang(lng_add_contact_button)),
_overDelete(false) {
connect(main, SIGNAL(dialogToTop(const History::DialogLinks&)), this, SLOT(onDialogToTop(const History::DialogLinks&)));
connect(main, SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)));
connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*)));
@@ -47,6 +51,18 @@ _addContactLnk(this, lang(lng_add_contact_button)) {
refresh(false);
}
int32 DialogsListWidget::filteredOffset() const {
return hashtagResults.size() * st::mentionHeight;
}
int32 DialogsListWidget::peopleOffset() const {
return filteredOffset() + (filterResults.size() * st::dlgHeight) + st::searchedBarHeight;
}
int32 DialogsListWidget::searchedOffset() const {
return peopleOffset() + (peopleResults.isEmpty() ? 0 : ((peopleResults.size() * st::dlgHeight) + st::searchedBarHeight));
}
void DialogsListWidget::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
bool trivial = (rect() == r);
@@ -70,10 +86,35 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
}
} else if (_state == FilteredState || _state == SearchedState) {
if (filterResults.isEmpty()) {
// .. paint no dialogs
} else {
int32 from = r.top() / int32(st::dlgHeight);
if (!hashtagResults.isEmpty()) {
int32 from = r.top() / int32(st::mentionHeight);
if (from < 0) {
from = 0;
} else if (from > hashtagResults.size()) {
from = hashtagResults.size();
}
p.translate(0, from * st::mentionHeight);
if (from < hashtagResults.size()) {
int32 to = (r.bottom() / int32(st::mentionHeight)) + 1, w = width();
if (to > hashtagResults.size()) to = hashtagResults.size();
p.setFont(st::mentionFont->f);
p.setPen(st::black->p);
for (; from < to; ++from) {
bool selected = (from == hashtagSel);
if (selected) {
p.fillRect(0, 0, w, st::mentionHeight, st::dlgHoverBG->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
p.drawPixmap(QPoint(w - st::notifyClose.icon.pxWidth() - skip, skip), App::sprite(), st::notifyClose.icon);
}
QString tag = st::mentionFont->m.elidedText('#' + hashtagResults.at(from), Qt::ElideRight, w - st::dlgPaddingHor * 2);
p.drawText(st::dlgPaddingHor, st::mentionTop + st::mentionFont->ascent, tag);
p.translate(0, st::mentionHeight);
}
}
}
if (!filterResults.isEmpty()) {
int32 skip = filteredOffset();
int32 from = (r.top() - skip) / int32(st::dlgHeight);
if (from < 0) {
from = 0;
} else if (from > filterResults.size()) {
@@ -99,7 +140,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), lang(lng_search_global_results), style::al_center);
p.translate(0, st::searchedBarHeight);
int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
int32 skip = peopleOffset();
int32 from = (r.top() - skip) / int32(st::dlgHeight);
if (from < 0) {
from = 0;
@@ -127,8 +168,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), text, style::al_center);
p.translate(0, st::searchedBarHeight);
int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
if (!peopleResults.isEmpty()) skip += peopleResults.size() * st::dlgHeight + st::searchedBarHeight;
int32 skip = searchedOffset();
int32 from = (r.top() - skip) / int32(st::dlgHeight);
if (from < 0) {
from = 0;
@@ -206,6 +246,7 @@ void DialogsListWidget::onUpdateSelected(bool force) {
if ((!force && !rect().contains(mouse)) || !selByMouse) return;
int w = width(), mouseY = mouse.y();
_overDelete = false;
if (_state == DefaultState) {
DialogRow *newSel = dialogs.list.rowAtY(mouseY, st::dlgHeight);
int32 otherStart = dialogs.list.count * st::dlgHeight;
@@ -221,8 +262,23 @@ void DialogsListWidget::onUpdateSelected(bool force) {
parentWidget()->update();
}
} else if (_state == FilteredState || _state == SearchedState) {
if (!hashtagResults.isEmpty()) {
int32 newHashtagSel = mouseY / int32(st::mentionHeight);
if (newHashtagSel < 0 || newHashtagSel >= hashtagResults.size()) {
newHashtagSel = -1;
}
if (newHashtagSel != hashtagSel) {
hashtagSel = newHashtagSel;
setCursor((hashtagSel >= 0) ? style::cur_pointer : style::cur_default);
parentWidget()->update();
}
if (hashtagSel >= 0) {
_overDelete = (mouse.x() >= w - st::mentionHeight);
}
mouseY -= filteredOffset();
}
if (!filterResults.isEmpty()) {
int32 newFilteredSel = mouseY / int32(st::dlgHeight);
int32 newFilteredSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
if (newFilteredSel < 0 || newFilteredSel >= filterResults.size()) {
newFilteredSel = -1;
}
@@ -232,7 +288,7 @@ void DialogsListWidget::onUpdateSelected(bool force) {
parentWidget()->update();
}
}
mouseY -= filterResults.size() * st::dlgHeight + st::searchedBarHeight;
mouseY -= peopleOffset() - filteredOffset();
if (!peopleResults.isEmpty()) {
int32 newPeopleSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
if (newPeopleSel < 0 || newPeopleSel >= peopleResults.size()) {
@@ -243,8 +299,8 @@ void DialogsListWidget::onUpdateSelected(bool force) {
setCursor((peopleSel >= 0) ? style::cur_pointer : style::cur_default);
parentWidget()->update();
}
mouseY -= peopleResults.size() * st::dlgHeight + st::searchedBarHeight;
}
mouseY -= searchedOffset() - peopleOffset();
if (_state == SearchedState && !searchResults.isEmpty()) {
int32 newSearchedSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
if (newSearchedSel < 0 || newSearchedSel >= searchResults.size()) {
@@ -369,7 +425,7 @@ void DialogsListWidget::dlgUpdated(History *history) {
}
}
} else if (_state == FilteredState || _state == SearchedState) {
int32 cnt = 0;
int32 cnt = 0, add = filteredOffset();
for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) {
if ((*i)->history == history) {
update(0, cnt * st::dlgHeight, width(), st::dlgHeight);
@@ -378,7 +434,7 @@ void DialogsListWidget::dlgUpdated(History *history) {
++cnt;
}
if (!peopleResults.isEmpty()) {
int32 cnt = 0, add = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
int32 cnt = 0, add = peopleOffset();
for (PeopleResults::const_iterator i = peopleResults.cbegin(), e = peopleResults.cend(); i != e; ++i) {
if ((*i) == history->peer) {
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
@@ -388,7 +444,7 @@ void DialogsListWidget::dlgUpdated(History *history) {
}
}
if (!searchResults.isEmpty()) {
int32 cnt = 0, add = (filterResults.size() + peopleResults.size()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight;
int32 cnt = 0, add = searchedOffset();
for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) {
if ((*i)->_item->history() == history) {
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
@@ -408,9 +464,10 @@ void DialogsListWidget::enterEvent(QEvent *e) {
void DialogsListWidget::leaveEvent(QEvent *e) {
setMouseTracking(false);
if (sel || filteredSel >= 0) {
selByMouse = false;
if (sel || filteredSel >= 0 || hashtagSel >= 0 || searchedSel >= 0 || peopleSel >= 0) {
sel = 0;
filteredSel = -1;
filteredSel = searchedSel = peopleSel = hashtagSel = -1;
parentWidget()->update();
}
}
@@ -462,6 +519,7 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
filter = newFilter;
if (filter.isEmpty()) {
_state = DefaultState;
hashtagResults.clear();
filterResults.clear();
peopleResults.clear();
searchResults.clear();
@@ -549,6 +607,33 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
}
}
void DialogsListWidget::onHashtagFilterUpdate(QString newFilter) {
if (newFilter.isEmpty() || newFilter.at(0) != '#') {
if (!hashtagResults.isEmpty()) {
hashtagResults.clear();
refresh(true);
setMouseSel(false, true);
}
return;
}
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
Local::readRecentHashtags();
}
const RecentHashtagPack &recent(cRecentSearchHashtags());
hashtagResults.clear();
if (!recent.isEmpty()) {
hashtagResults.reserve(qMin(recent.size(), 5));
for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
if (i->first.startsWith(newFilter.midRef(1), Qt::CaseInsensitive) && i->first.size() + 1 != newFilter.size()) {
hashtagResults.push_back(i->first);
if (hashtagResults.size() == 5) break;
}
}
}
refresh(true);
setMouseSel(false, true);
}
DialogsListWidget::~DialogsListWidget() {
clearSearchResults();
}
@@ -634,7 +719,11 @@ void DialogsListWidget::peopleReceived(const QString &query, const QVector<MTPCo
void DialogsListWidget::contactsReceived(const QVector<MTPContact> &contacts) {
cSetContactsReceived(true);
for (QVector<MTPContact>::const_iterator i = contacts.cbegin(), e = contacts.cend(); i != e; ++i) {
addNewContact(i->c_contact().vuser_id.v);
int32 uid = i->c_contact().vuser_id.v;
addNewContact(uid);
if (uid == MTP::authedId() && App::self()) {
App::self()->contact = 1;
}
}
if (!sel && contactsNoDialogs.list.count) {
sel = contactsNoDialogs.list.begin;
@@ -657,6 +746,7 @@ int32 DialogsListWidget::addNewContact(int32 uid, bool select) {
sel = added;
contactSel = true;
}
if (contactsNoDialogs.list.count == 1 && !dialogs.list.count) refresh();
return added ? ((dialogs.list.count + added->pos) * st::dlgHeight) : -1;
}
if (select) {
@@ -683,9 +773,9 @@ void DialogsListWidget::refresh(bool toTop) {
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
if (_state == FilteredState) {
h = (filterResults.count() + peopleResults.count() + searchResults.count()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + (searchResults.isEmpty() ? 0 : st::searchedBarHeight);
h = searchedOffset() + (searchResults.count() * st::dlgHeight) + (searchResults.isEmpty() ? 0 : st::searchedBarHeight);
} else if (_state == SearchedState) {
h = (filterResults.count() + peopleResults.count() + searchResults.count()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight;
h = searchedOffset() + (searchResults.count() * st::dlgHeight) + st::searchedBarHeight;
}
}
resize(width(), h);
@@ -703,7 +793,7 @@ void DialogsListWidget::setMouseSel(bool msel, bool toTop) {
sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0));
contactSel = !dialogs.list.count && contactsNoDialogs.list.count;
} else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search
filteredSel = peopleSel = searchedSel = -1;
filteredSel = peopleSel = searchedSel = hashtagSel = -1;
}
}
}
@@ -712,8 +802,10 @@ void DialogsListWidget::setState(State newState) {
_state = newState;
if (_state == DefaultState) {
clearSearchResults();
searchedSel = peopleSel = filteredSel = -1;
searchedSel = peopleSel = filteredSel = hashtagSel = -1;
} else if (_state == DefaultState || _state == SearchedState) {
hashtagResults.clear();
hashtagSel = -1;
filterResults.clear();
filteredSel = -1;
}
@@ -726,12 +818,13 @@ DialogsListWidget::State DialogsListWidget::state() const {
}
bool DialogsListWidget::hasFilteredResults() const {
return !filterResults.isEmpty();
return !filterResults.isEmpty() && hashtagResults.isEmpty();
}
void DialogsListWidget::clearFilter() {
if (_state == FilteredState || _state == SearchedState) {
_state = DefaultState;
hashtagResults.clear();
filterResults.clear();
peopleResults.clear();
searchResults.clear();
@@ -742,7 +835,7 @@ void DialogsListWidget::clearFilter() {
}
void DialogsListWidget::addDialog(const MTPDdialog &dialog) {
History *history = App::history(App::peerFromMTP(dialog.vpeer), dialog.vunread_count.v);
History *history = App::history(App::peerFromMTP(dialog.vpeer), dialog.vunread_count.v, dialog.vread_inbox_max_id.v);
History::DialogLinks links = dialogs.addToEnd(history);
history->dialogs = links;
contactsNoDialogs.del(history->peer);
@@ -778,37 +871,45 @@ void DialogsListWidget::selectSkip(int32 direction) {
int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight;
emit mustScrollTo(fromY, fromY + st::dlgHeight);
} else if (_state == FilteredState || _state == SearchedState) {
if (filterResults.isEmpty() && peopleResults.isEmpty() && searchResults.isEmpty()) return;
if ((filteredSel < 0 || filteredSel >= filterResults.size()) &&
if (hashtagResults.isEmpty() && filterResults.isEmpty() && peopleResults.isEmpty() && searchResults.isEmpty()) return;
if ((hashtagSel < 0 || hashtagSel >= hashtagResults.size()) &&
(filteredSel < 0 || filteredSel >= filterResults.size()) &&
(peopleSel < 0 || peopleSel >= peopleResults.size()) &&
(searchedSel < 0 || searchedSel >= searchResults.size())) {
if (filterResults.isEmpty() && peopleResults.isEmpty()) {
if (hashtagResults.isEmpty() && filterResults.isEmpty() && peopleResults.isEmpty()) {
searchedSel = 0;
} else if (filterResults.isEmpty()) {
} else if (hashtagResults.isEmpty() && filterResults.isEmpty()) {
peopleSel = 0;
} else {
} else if (hashtagResults.isEmpty()) {
filteredSel = 0;
} else {
hashtagSel = 0;
}
} else {
int32 cur = (filteredSel >= 0 && filteredSel < filterResults.size()) ? filteredSel : ((peopleSel >= 0 && peopleSel < peopleResults.size()) ? (peopleSel + filterResults.size()) : (searchedSel + peopleResults.size() + filterResults.size()));
cur = snap(cur + direction, 0, filterResults.size() + peopleResults.size() + searchResults.size() - 1);
if (cur < filterResults.size()) {
filteredSel = cur;
peopleSel = searchedSel = -1;
} else if (cur < filterResults.size() + peopleResults.size()) {
peopleSel = cur - filterResults.size();
filteredSel = searchedSel = -1;
int32 cur = (hashtagSel >= 0 && hashtagSel < hashtagResults.size()) ? hashtagSel : ((filteredSel >= 0 && filteredSel < filterResults.size()) ? (hashtagResults.size() + filteredSel) : ((peopleSel >= 0 && peopleSel < peopleResults.size()) ? (peopleSel + filterResults.size() + hashtagResults.size()) : (searchedSel + peopleResults.size() + filterResults.size() + hashtagResults.size())));
cur = snap(cur + direction, 0, hashtagResults.size() + filterResults.size() + peopleResults.size() + searchResults.size() - 1);
if (cur < hashtagResults.size()) {
hashtagSel = cur;
filteredSel = peopleSel = searchedSel = -1;
} else if (cur < hashtagResults.size() + filterResults.size()) {
filteredSel = cur - hashtagResults.size();
hashtagSel = peopleSel = searchedSel = -1;
} else if (cur < hashtagResults.size() + filterResults.size() + peopleResults.size()) {
peopleSel = cur - hashtagResults.size() - filterResults.size();
hashtagSel = filteredSel = searchedSel = -1;
} else {
filteredSel = peopleSel = -1;
searchedSel = cur - filterResults.size() - peopleResults.size();
hashtagSel = filteredSel = peopleSel = -1;
searchedSel = cur - hashtagResults.size() - filterResults.size() - peopleResults.size();
}
}
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
emit mustScrollTo(filteredSel * st::dlgHeight, (filteredSel + 1) * st::dlgHeight);
if (hashtagSel >= 0 && hashtagSel < hashtagResults.size()) {
emit mustScrollTo(hashtagSel * st::mentionHeight, (hashtagSel + 1) * st::mentionHeight);
} else if (filteredSel >= 0 && filteredSel < filterResults.size()) {
emit mustScrollTo(filteredOffset() + filteredSel * st::dlgHeight, filteredOffset() + (filteredSel + 1) * st::dlgHeight);
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
emit mustScrollTo((peopleSel + filterResults.size()) * st::dlgHeight + (peopleSel ? st::searchedBarHeight : 0), (peopleSel + filterResults.size() + 1) * st::dlgHeight);
emit mustScrollTo(peopleOffset() + peopleSel * st::dlgHeight + (peopleSel ? 0 : -st::searchedBarHeight), peopleOffset() + (peopleSel + 1) * st::dlgHeight);
} else {
emit mustScrollTo((searchedSel + peopleResults.size() + filterResults.size()) * st::dlgHeight + (peopleResults.size() ? st::searchedBarHeight : 0) + (searchedSel ? st::searchedBarHeight : 0), (searchedSel + peopleResults.size() + filterResults.size() + 1) * st::dlgHeight + (peopleResults.size() ? st::searchedBarHeight : 0) + st::searchedBarHeight);
emit mustScrollTo(searchedOffset() + searchedSel * st::dlgHeight + (searchedSel ? 0 : -st::searchedBarHeight), searchedOffset() + (searchedSel + 1) * st::dlgHeight);
}
}
parentWidget()->update();
@@ -830,7 +931,7 @@ void DialogsListWidget::scrollToPeer(const PeerId &peer, MsgId msgId) {
if (msgId) {
for (int32 i = 0, c = searchResults.size(); i < c; ++i) {
if (searchResults[i]->_item->history()->peer->id == peer && searchResults[i]->_item->id == msgId) {
fromY = filterResults.size() * st::dlgHeight + st::searchedBarHeight + i * st::dlgHeight;
fromY = searchedOffset() + i * st::dlgHeight;
break;
}
}
@@ -838,7 +939,7 @@ void DialogsListWidget::scrollToPeer(const PeerId &peer, MsgId msgId) {
if (fromY < 0) {
for (int32 i = 0, c = filterResults.size(); i < c; ++i) {
if (filterResults[i]->history->peer->id == peer) {
fromY = i * st::dlgHeight;
fromY = filteredOffset() + (i * st::dlgHeight);
break;
}
}
@@ -914,7 +1015,7 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) {
}
}
} else if (_state == FilteredState || _state == SearchedState) {
int32 from = yFrom / st::dlgHeight;
int32 from = (yFrom - filteredOffset()) / st::dlgHeight;
if (from < 0) from = 0;
if (from < filterResults.size()) {
int32 to = (yTo / int32(st::dlgHeight)) + 1, w = width();
@@ -925,20 +1026,20 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) {
}
}
from = (yFrom > st::searchedBarHeight ? ((yFrom - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size();
from = (yFrom > filteredOffset() + st::searchedBarHeight ? ((yFrom - filteredOffset() - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size();
if (from < 0) from = 0;
if (from < peopleResults.size()) {
int32 to = (yTo > st::searchedBarHeight ? ((yTo - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() + 1, w = width();
int32 to = (yTo > filteredOffset() + st::searchedBarHeight ? ((yTo - filteredOffset() - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() + 1, w = width();
if (to > peopleResults.size()) to = peopleResults.size();
for (; from < to; ++from) {
peopleResults[from]->photo->load();
}
}
from = (yFrom > ((peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size();
from = (yFrom > filteredOffset() + ((peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - filteredOffset() - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size();
if (from < 0) from = 0;
if (from < searchResults.size()) {
int32 to = (yTo >(peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size() + 1, w = width();
int32 to = (yTo > filteredOffset() + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - filteredOffset() - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size() + 1, w = width();
if (to > searchResults.size()) to = searchResults.size();
for (; from < to; ++from) {
@@ -954,6 +1055,31 @@ bool DialogsListWidget::choosePeer() {
if (_state == DefaultState) {
if (sel) history = sel->history;
} else if (_state == FilteredState || _state == SearchedState) {
if (hashtagSel >= 0 && hashtagSel < hashtagResults.size()) {
QString hashtag = hashtagResults.at(hashtagSel);
if (_overDelete) {
lastMousePos = QCursor::pos();
RecentHashtagPack recent(cRecentSearchHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == hashtag) {
i = recent.erase(i);
} else {
++i;
}
}
cSetRecentSearchHashtags(recent);
Local::writeRecentHashtags();
emit refreshHashtags();
selByMouse = true;
onUpdateSelected(true);
} else {
saveRecentHashtags('#' + hashtag);
emit completeHashtag(hashtag);
}
return true;
}
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
history = filterResults[filteredSel]->history;
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
@@ -964,22 +1090,55 @@ bool DialogsListWidget::choosePeer() {
}
}
if (history) {
bool chosen = (!App::main()->selectingPeer() && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
if (msgId) {
saveRecentHashtags(filter);
}
bool chosen = (!App::main()->selectingPeer(true) && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
App::main()->showPeer(history->peer->id, msgId);
if (chosen) {
emit searchResultChosen();
}
sel = 0;
filteredSel = peopleSel = searchedSel = -1;
filteredSel = peopleSel = searchedSel = hashtagSel = -1;
parentWidget()->update();
return true;
}
return false;
}
void DialogsListWidget::saveRecentHashtags(const QString &text) {
bool found = false;
QRegularExpressionMatch m;
RecentHashtagPack recent(cRecentSearchHashtags());
for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) {
i = m.capturedStart();
next = m.capturedEnd();
if (m.hasMatch()) {
if (!m.capturedRef(1).isEmpty()) {
++i;
}
if (!m.capturedRef(2).isEmpty()) {
--next;
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentStickers();
recent = cRecentSearchHashtags();
}
found = true;
incrementRecentHashtag(recent, text.mid(i + 1, next - i - 1));
}
if (found) {
cSetRecentSearchHashtags(recent);
Local::writeRecentHashtags();
}
}
void DialogsListWidget::destroyData() {
sel = 0;
contactSel = false;
hashtagSel = -1;
hashtagResults.clear();
filteredSel = -1;
filterResults.clear();
filter.clear();
@@ -1193,11 +1352,14 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent)
connect(&list, SIGNAL(dialogToTopFrom(int)), this, SLOT(onDialogToTopFrom(int)));
connect(&list, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages()));
connect(&list, SIGNAL(searchResultChosen()), this, SLOT(onCancel()));
connect(&list, SIGNAL(completeHashtag(QString)), this, SLOT(onCompleteHashtag(QString)));
connect(&list, SIGNAL(refreshHashtags()), this, SLOT(onFilterCursorMoved()));
connect(&scroll, SIGNAL(geometryChanged()), &list, SLOT(onParentGeometryChanged()));
connect(&scroll, SIGNAL(scrolled()), &list, SLOT(onUpdateSelected()));
connect(&scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
connect(&_filter, SIGNAL(cancelled()), this, SLOT(onCancel()));
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
connect(&_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int)));
connect(parent, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll()));
connect(&_addContact, SIGNAL(clicked()), this, SLOT(onAddContact()));
connect(&_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup()));
@@ -1359,8 +1521,10 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs) {
}
}
bool DialogsWidget::dialogsFailed(const RPCError &e) {
LOG(("RPC Error: %1 %2: %3").arg(e.code()).arg(e.type()).arg(e.description()));
bool DialogsWidget::dialogsFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("RPC Error: %1 %2: %3").arg(error.code()).arg(error.type()).arg(error.description()));
dlgPreloading = 0;
return true;
}
@@ -1424,6 +1588,8 @@ void DialogsWidget::searchMessages(const QString &query) {
onFilterUpdate();
_searchTimer.stop();
onSearchMessages();
list.saveRecentHashtags(query);
}
}
@@ -1452,7 +1618,9 @@ void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &contacts) {
}
}
bool DialogsWidget::contactsFailed() {
bool DialogsWidget::contactsFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
return true;
}
@@ -1518,6 +1686,8 @@ void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId
}
bool DialogsWidget::searchFailed(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (_searchRequest == req) {
_searchRequest = 0;
_searchFull = true;
@@ -1526,6 +1696,8 @@ bool DialogsWidget::searchFailed(const RPCError &error, mtpRequestId req) {
}
bool DialogsWidget::peopleFailed(const RPCError &error, mtpRequestId req) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (_peopleRequest == req) {
_peopleRequest = 0;
_peopleFull = true;
@@ -1578,6 +1750,48 @@ void DialogsWidget::onFilterUpdate(bool force) {
}
}
void DialogsWidget::onFilterCursorMoved(int from, int to) {
if (to < 0) to = _filter.cursorPosition();
QString t = _filter.text(), r;
for (int start = to; start > 0;) {
--start;
if (t.size() <= start) break;
if (t.at(start) == '#') {
r = t.mid(start, to - start);
break;
}
if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break;
}
list.onHashtagFilterUpdate(r);
}
void DialogsWidget::onCompleteHashtag(QString tag) {
QString t = _filter.text(), r;
int cur = _filter.cursorPosition();
for (int start = cur; start > 0;) {
--start;
if (t.size() <= start) break;
if (t.at(start) == '#') {
if (cur == start + 1 || t.midRef(start + 1, cur - start - 1) == tag.midRef(0, cur - start - 1)) {
for (; cur < t.size() && cur - start - 1 < tag.size(); ++cur) {
if (t.at(cur) != tag.at(cur - start - 1)) break;
}
if (cur - start - 1 == tag.size() && cur < t.size() && t.at(cur) == ' ') ++cur;
r = t.mid(0, start + 1) + tag + ' ' + t.mid(cur);
_filter.setText(r);
_filter.setCursorPosition(start + 1 + tag.size() + 1);
onFilterUpdate(true);
return;
}
break;
}
if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break;
}
_filter.setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur));
_filter.setCursorPosition(cur + 1 + tag.size() + 1);
onFilterUpdate(true);
}
void DialogsWidget::resizeEvent(QResizeEvent *e) {
int32 w = width() - st::dlgShadow;
_filter.setGeometry(st::dlgPaddingHor, st::dlgFilterPadding, w - 2 * st::dlgPaddingHor, _filter.height());
@@ -1677,7 +1891,7 @@ void DialogsWidget::onAddContact() {
}
void DialogsWidget::onNewGroup() {
App::wnd()->showLayer(new NewGroupBox());
App::wnd()->showLayer(new ContactsBox(true));
}
bool DialogsWidget::onCancelSearch() {

View File

@@ -36,6 +36,10 @@ public:
void contactsReceived(const QVector<MTPContact> &contacts);
int32 addNewContact(int32 uid, bool sel = false); // return y of row or -1 if failed
int32 filteredOffset() const;
int32 peopleOffset() const;
int32 searchedOffset() const;
void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
@@ -59,6 +63,7 @@ public:
void refresh(bool toTop = false);
bool choosePeer();
void saveRecentHashtags(const QString &text);
void destroyData();
@@ -89,6 +94,7 @@ public:
bool hasFilteredResults() const;
void onFilterUpdate(QString newFilter, bool force = false);
void onHashtagFilterUpdate(QString newFilter);
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
@@ -109,6 +115,8 @@ signals:
void dialogToTopFrom(int movedFrom);
void searchMessages();
void searchResultChosen();
void completeHashtag(QString tag);
void refreshHashtags();
private:
@@ -123,6 +131,10 @@ private:
bool selByMouse;
QString filter;
QStringList hashtagResults;
int32 hashtagSel;
FilteredDialogs filterResults;
int32 filteredSel;
@@ -143,6 +155,8 @@ private:
LinkButton _addContactLnk;
bool _overDelete;
};
class DialogsWidget : public QWidget, public Animated, public RPCSender {
@@ -206,6 +220,9 @@ public slots:
void onNewGroup();
bool onCancelSearch();
void onFilterCursorMoved(int from = -1, int to = -1);
void onCompleteHashtag(QString tag);
void onDialogToTopFrom(int movedFrom);
bool onSearchMessages(bool searchCache = false);
void onNeedSearchMessages();
@@ -215,8 +232,8 @@ private:
bool _drawShadow;
void unreadCountsReceived(const QVector<MTPDialog> &dialogs);
bool dialogsFailed(const RPCError &e);
bool contactsFailed();
bool dialogsFailed(const RPCError &error);
bool contactsFailed(const RPCError &error);
bool searchFailed(const RPCError &error, mtpRequestId req);
bool peopleFailed(const RPCError &error, mtpRequestId req);

View File

@@ -24,6 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "window.h"
#include "apiwrap.h"
Dropdown::Dropdown(QWidget *parent) : TWidget(parent),
_hiding(false), a_opacity(0), _shadow(st::dropdownShadow) {
@@ -160,7 +161,7 @@ void Dropdown::showStart() {
}
bool Dropdown::animStep(float64 ms) {
float64 dt = ms / 150;
float64 dt = ms / st::dropdownDuration;
bool res = true;
if (dt >= 1) {
a_opacity.finish();
@@ -311,7 +312,7 @@ void DragArea::showStart() {
}
bool DragArea::animStep(float64 ms) {
float64 dt = ms / 150;
float64 dt = ms / st::dropdownDuration;
bool res = true;
if (dt >= 1) {
a_opacity.finish();
@@ -764,7 +765,7 @@ void EmojiPan::fastHide() {
}
bool EmojiPan::animStep(float64 ms) {
float64 dt = ms / 150;
float64 dt = ms / st::dropdownDuration;
bool res = true;
if (dt >= 1) {
a_opacity.finish();
@@ -880,6 +881,410 @@ void EmojiPan::onTabChange() {
}
}
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows) : _parent(parent), _rows(rows), _hrows(hrows), _sel(-1), _mouseSel(false), _overDelete(false) {
}
void MentionsInner::paintEvent(QPaintEvent *e) {
QPainter p(this);
int32 atwidth = st::mentionFont->m.width('@'), hashwidth = st::mentionFont->m.width('#');
int32 availwidth = width() - 2 * st::mentionPadding.left() - st::mentionPhotoSize - 2 * st::mentionPadding.right();
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::dlgShadow, htagwidth = width() - st::mentionPadding.right() - htagleft;
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1, last = _rows->isEmpty() ? _hrows->size() : _rows->size();
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
if (i == _sel) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::dlgHoverBG->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (_rows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
}
p.setPen(st::black->p);
if (_rows->isEmpty()) {
QString tag = st::mentionFont->m.elidedText('#' + _hrows->at(last - i - 1), Qt::ElideRight, htagwidth);
p.setFont(st::mentionFont->f);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, tag);
} else {
UserData *user = _rows->at(last - i - 1);
QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (availwidth < unamewidth + namewidth) {
namewidth = (availwidth * namewidth) / (namewidth + unamewidth);
unamewidth = availwidth - namewidth;
if (firstwidth <= unamewidth) {
if (firstwidth < unamewidth) {
first = st::mentionFont->m.elidedText(first, Qt::ElideRight, unamewidth);
} else if (!second.isEmpty()) {
first = st::mentionFont->m.elidedText(first + second, Qt::ElideRight, unamewidth);
second = QString();
}
} else {
second = st::mentionFont->m.elidedText(second, Qt::ElideRight, unamewidth - firstwidth);
}
}
user->photo->load();
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pix(st::mentionPhotoSize));
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
p.setFont(st::mentionFont->f);
p.setPen(st::profileOnlineColor->p);
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
if (!second.isEmpty()) {
p.setPen(st::profileOfflineColor->p);
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
}
}
p.fillRect(cWideMode() ? st::dlgShadow : 0, _parent->innerTop(), width() - (cWideMode() ? st::dlgShadow : 0), st::titleShadow, st::titleShadowColor->b);
p.fillRect(cWideMode() ? st::dlgShadow : 0, _parent->innerBottom() - st::titleShadow, width() - (cWideMode() ? st::dlgShadow : 0), st::titleShadow, st::titleShadowColor->b);
}
void MentionsInner::mouseMoveEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
}
void MentionsInner::clearSel() {
_mouseSel = _overDelete = false;
setSel(-1);
}
bool MentionsInner::moveSel(int direction) {
_mouseSel = false;
int32 maxSel = (_rows->isEmpty() ? _hrows->size() : _rows->size());
if (_sel >= maxSel || _sel < 0) {
if (direction < 0) setSel(maxSel - 1, true);
return (_sel >= 0 && _sel < maxSel);
}
if (_sel > 0 || direction > 0) {
setSel((_sel + direction >= maxSel) ? -1 : (_sel + direction), true);
}
return true;
}
bool MentionsInner::select() {
int32 maxSel = (_rows->isEmpty() ? _hrows->size() : _rows->size());
if (_sel >= 0 && _sel < maxSel) {
QString result = _rows->isEmpty() ? ('#' + _hrows->at(_hrows->size() - _sel - 1)) : ('@' + _rows->at(_rows->size() - _sel - 1)->username);
emit chosen(result);
return true;
}
return false;
}
void MentionsInner::mousePressEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
if (e->button() == Qt::LeftButton) {
if (_overDelete && _sel >= 0 && _sel < _hrows->size()) {
_mousePos = mapToGlobal(e->pos());
QString toRemove = _hrows->at(_hrows->size() - _sel - 1);
RecentHashtagPack recent(cRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == toRemove) {
i = recent.erase(i);
} else {
++i;
}
}
cSetRecentWriteHashtags(recent);
Local::writeRecentHashtags();
_parent->updateFiltered();
_mouseSel = true;
onUpdateSelected(true);
} else {
select();
}
}
}
void MentionsInner::enterEvent(QEvent *e) {
setMouseTracking(true);
_mousePos = QCursor::pos();
onUpdateSelected(true);
}
void MentionsInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
if (_sel >= 0) {
setSel(-1);
}
}
void MentionsInner::setSel(int sel, bool scroll) {
_sel = sel;
parentWidget()->update();
int32 maxSel = _rows->isEmpty() ? _hrows->size() : _rows->size();
if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
}
void MentionsInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(_mousePos));
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
int w = width(), mouseY = mouse.y();
_overDelete = _rows->isEmpty() && (mouse.x() >= w - st::mentionHeight);
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _rows->isEmpty() ? _hrows->size() : _rows->size();
if (sel < 0 || sel >= maxSel) {
sel = -1;
}
if (sel != _sel) {
setSel(sel);
}
}
void MentionsInner::onParentGeometryChanged() {
_mousePos = QCursor::pos();
if (rect().contains(mapFromGlobal(_mousePos))) {
setMouseTracking(true);
onUpdateSelected(true);
}
}
MentionsDropdown::MentionsDropdown(QWidget *parent) : QWidget(parent),
_scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows), _chat(0), _hiding(false), a_opacity(0), _shadow(st::dropdownShadow) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString)));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
setFocusPolicy(Qt::NoFocus);
_scroll.setFocusPolicy(Qt::NoFocus);
_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
_inner.setGeometry(rect());
_scroll.setGeometry(rect());
_scroll.setWidget(&_inner);
_scroll.show();
_inner.show();
connect(&_scroll, SIGNAL(geometryChanged()), &_inner, SLOT(onParentGeometryChanged()));
connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(onUpdateSelected()));
if (cPlatform() == dbipMac) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
}
void MentionsDropdown::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
p.fillRect(rect(), st::white->b);
}
void MentionsDropdown::showFiltered(ChatData *chat, QString start) {
_chat = chat;
start = start.toLower();
bool toDown = (_filter != start);
if (toDown) {
_filter = start;
}
updateFiltered(toDown);
}
void MentionsDropdown::updateFiltered(bool toDown) {
int32 now = unixtime();
QMultiMap<int32, UserData*> ordered;
MentionRows rows;
HashtagRows hrows;
if (_filter.at(0) == '@') {
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
if (_chat->participants.isEmpty()) {
if (_chat->count > 0) {
App::api()->requestFullPeer(_chat);
}
} else {
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
ordered.insertMulti(App::onlineForSort(user->onlineTill, now), user);
}
}
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
rows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user->onlineTill, now), user);
}
}
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
rows.push_back(i.value());
}
}
} else {
const RecentHashtagPack &recent(cRecentWriteHashtags());
hrows.reserve(recent.size());
for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
if (_filter.size() > 1 && (!i->first.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || i->first.size() + 1 == _filter.size())) continue;
hrows.push_back(i->first);
}
}
if (rows.isEmpty() && hrows.isEmpty()) {
if (!isHidden()) {
hideStart();
_rows.clear();
_hrows.clear();
}
} else {
_rows = rows;
_hrows = hrows;
bool hidden = _hiding || isHidden();
if (hidden) {
show();
_scroll.show();
}
recount(toDown);
if (hidden) {
hide();
showStart();
}
}
}
void MentionsDropdown::setBoundings(QRect boundings) {
_boundings = boundings;
resize(_boundings.width(), height());
_scroll.resize(size());
_inner.resize(width(), _inner.height());
recount();
}
void MentionsDropdown::recount(bool toDown) {
int32 h = (_rows.isEmpty() ? _hrows.size() : _rows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
if (_inner.height() != h) {
st += h - _inner.height();
_inner.resize(width(), h);
}
if (h > _boundings.height()) h = _boundings.height();
if (h > 5 * st::mentionHeight) h = 5 * st::mentionHeight;
if (height() != h) {
st += _scroll.height() - h;
setGeometry(0, _boundings.height() - h, width(), h);
_scroll.resize(width(), h);
} else if (y() != _boundings.height() - h) {
move(0, _boundings.height() - h);
}
if (toDown) st = _scroll.scrollTopMax();
if (st != oldst) _scroll.scrollToY(st);
if (toDown) _inner.clearSel();
}
void MentionsDropdown::fastHide() {
if (animating()) {
anim::stop(this);
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
hideFinish();
}
void MentionsDropdown::hideStart() {
if (!_hiding) {
if (_cache.isNull()) {
_scroll.show();
_cache = myGrab(this, rect());
}
_scroll.hide();
_hiding = true;
a_opacity.start(0);
anim::start(this);
}
}
void MentionsDropdown::hideFinish() {
hide();
_hiding = false;
_filter = qsl("-");
_inner.clearSel();
}
void MentionsDropdown::showStart() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) {
return;
}
if (_cache.isNull()) {
_scroll.show();
_cache = myGrab(this, rect());
}
_scroll.hide();
_hiding = false;
show();
a_opacity.start(1);
anim::start(this);
}
bool MentionsDropdown::animStep(float64 ms) {
float64 dt = ms / st::dropdownDuration;
bool res = true;
if (dt >= 1) {
a_opacity.finish();
_cache = QPixmap();
if (_hiding) {
hideFinish();
} else {
_scroll.show();
_inner.clearSel();
}
res = false;
} else {
a_opacity.update(dt, anim::linear);
}
update();
return res;
}
const QString &MentionsDropdown::filter() const {
return _filter;
}
int32 MentionsDropdown::innerTop() {
return _scroll.scrollTop();
}
int32 MentionsDropdown::innerBottom() {
return _scroll.scrollTop() + _scroll.height();
}
bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
if (isHidden()) return QWidget::eventFilter(obj, e);
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
if (ev->key() == Qt::Key_Up) {
_inner.moveSel(-1);
return true;
} else if (ev->key() == Qt::Key_Down) {
return _inner.moveSel(1);
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Space) {
return _inner.select();
}
}
return QWidget::eventFilter(obj, e);
}
MentionsDropdown::~MentionsDropdown() {
}
//StickerPanInner::StickerPanInner(QWidget *parent) : QWidget(parent), _emoji(0), _selected(-1), _pressedSel(-1) {
// resize(StickerPadPerRow * st::stickerPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
// setMouseTracking(true);
@@ -1124,7 +1529,7 @@ void EmojiPan::onTabChange() {
//}
//
//bool StickerPan::animStep(float64 ms) {
// float64 dt = ms / 150;
// float64 dt = ms / st::dropdownDuration;
// bool res = true;
// if (dt >= 1) {
// a_opacity.finish();

View File

@@ -227,6 +227,116 @@ private:
};
typedef QList<UserData*> MentionRows;
typedef QList<QString> HashtagRows;
class MentionsDropdown;
class MentionsInner : public QWidget {
Q_OBJECT
public:
MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows);
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void clearSel();
bool moveSel(int direction);
bool select();
signals:
void chosen(QString mentionOrHashtag);
void mustScrollTo(int scrollToTop, int scrollToBottom);
public slots:
void onParentGeometryChanged();
void onUpdateSelected(bool force = false);
private:
void setSel(int sel, bool scroll = false);
MentionsDropdown *_parent;
MentionRows *_rows;
HashtagRows *_hrows;
int32 _sel;
bool _mouseSel;
QPoint _mousePos;
bool _overDelete;
};
class MentionsDropdown : public QWidget, public Animated {
Q_OBJECT
public:
MentionsDropdown(QWidget *parent);
void paintEvent(QPaintEvent *e);
void fastHide();
void showFiltered(ChatData *chat, QString start);
void updateFiltered(bool toDown = false);
void setBoundings(QRect boundings);
bool animStep(float64 ms);
const QString &filter() const;
int32 innerTop();
int32 innerBottom();
bool eventFilter(QObject *obj, QEvent *e);
~MentionsDropdown();
signals:
void chosen(QString mentionOrHashtag);
public slots:
void hideStart();
void hideFinish();
void showStart();
private:
void recount(bool toDown = false);
QPixmap _cache;
MentionRows _rows;
HashtagRows _hrows;
ScrollArea _scroll;
MentionsInner _inner;
ChatData *_chat;
QString _filter;
QRect _boundings;
int32 _width, _height;
bool _hiding;
anim::fvalue a_opacity;
QTimer _hideTimer;
BoxShadow _shadow;
};
//class StickerPanInner : public QWidget, public Animated {
// Q_OBJECT
//

View File

@@ -256,7 +256,9 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
sendNext();
}
bool FileUploader::partFailed(const RPCError &err, mtpRequestId requestId) {
bool FileUploader::partFailed(const RPCError &error, mtpRequestId requestId) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
if (requestsSent.constFind(requestId) != requestsSent.cend() || docRequestsSent.constFind(requestId) != docRequestsSent.cend()) { // failed to upload current file
currentFailed();
}

View File

@@ -152,6 +152,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;

View File

@@ -23,26 +23,18 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "gui/countryinput.h"
#include "gui/scrollarea.h"
namespace {
struct CountryInfo {
CountryInfo(const char *_name, const char *_iso2, const char *_code) : name(_name), iso2(_iso2), code(_code) {
}
const char *name, *iso2, *code;
};
#include "countries.h"
typedef QHash<QString, const CountryInfo *> CountriesByCode;
typedef QHash<QString, const CountryInfo *> CountriesByISO2;
namespace {
typedef QList<const CountryInfo *> CountriesFiltered;
typedef QVector<int> CountriesIds;
typedef QHash<QChar, CountriesIds> CountriesByLetter;
typedef QVector<QString> CountryNames;
typedef QVector<CountryNames> CountriesNames;
CountriesByCode countriesByCode;
CountriesByISO2 countriesByISO2;
CountriesByCode _countriesByCode;
CountriesByISO2 _countriesByISO2;
CountriesFiltered countriesFiltered, countriesAll, *countriesNow = &countriesAll;
CountriesByLetter countriesByLetter;
CountriesNames countriesNames;
@@ -51,19 +43,19 @@ namespace {
int countriesCount = sizeof(countries) / sizeof(countries[0]);
void initCountries() {
if (countriesByCode.size()) return;
if (!_countriesByCode.isEmpty()) return;
countriesByCode.reserve(countriesCount);
countriesByISO2.reserve(countriesCount);
_countriesByCode.reserve(countriesCount);
_countriesByISO2.reserve(countriesCount);
for (int i = 0; i < countriesCount; ++i) {
const CountryInfo *info(countries + i);
countriesByCode.insert(info->code, info);
CountriesByISO2::const_iterator already = countriesByISO2.constFind(info->iso2);
if (already != countriesByISO2.cend()) {
_countriesByCode.insert(info->code, info);
CountriesByISO2::const_iterator already = _countriesByISO2.constFind(info->iso2);
if (already != _countriesByISO2.cend()) {
QString badISO = info->iso2;
(void)badISO;
}
countriesByISO2.insert(info->iso2, info);
_countriesByISO2.insert(info->iso2, info);
}
countriesAll.reserve(countriesCount);
countriesFiltered.reserve(countriesCount);
@@ -71,10 +63,20 @@ namespace {
}
}
const CountriesByCode &countriesByCode() {
initCountries();
return _countriesByCode;
}
const CountriesByISO2 &countriesByISO2() {
initCountries();
return _countriesByISO2;
}
QString findValidCode(QString fullCode) {
while (fullCode.length()) {
CountriesByCode::const_iterator i = countriesByCode.constFind(fullCode);
if (i != countriesByCode.cend()) {
CountriesByCode::const_iterator i = _countriesByCode.constFind(fullCode);
if (i != _countriesByCode.cend()) {
return (*i)->code;
}
fullCode = fullCode.mid(0, fullCode.length() - 1);
@@ -159,8 +161,8 @@ void CountryInput::onChooseCode(const QString &code) {
emit selectClosed();
}
if (code.length()) {
CountriesByCode::const_iterator i = countriesByCode.constFind(code);
if (i != countriesByCode.cend()) {
CountriesByCode::const_iterator i = _countriesByCode.constFind(code);
if (i != _countriesByCode.cend()) {
const CountryInfo *info = *i;
lastValidISO = info->iso2;
setText(QString::fromUtf8(info->name));
@@ -174,8 +176,8 @@ void CountryInput::onChooseCode(const QString &code) {
}
bool CountryInput::onChooseCountry(const QString &iso) {
CountriesByISO2::const_iterator i = countriesByISO2.constFind(iso);
const CountryInfo *info = (i == countriesByISO2.cend()) ? 0 : (*i);
CountriesByISO2::const_iterator i = _countriesByISO2.constFind(iso);
const CountryInfo *info = (i == _countriesByISO2.cend()) ? 0 : (*i);
if (info) {
lastValidISO = info->iso2;
@@ -206,12 +208,12 @@ CountryInput::~CountryInput() {
CountryList::CountryList(QWidget *parent, const style::countryList &st) : QWidget(parent), _sel(0),
_st(st), _mouseSel(false) {
CountriesByISO2::const_iterator l = countriesByISO2.constFind(lastValidISO);
CountriesByISO2::const_iterator l = _countriesByISO2.constFind(lastValidISO);
bool seenLastValid = false;
int already = countriesAll.size();
countriesByLetter.clear();
const CountryInfo *lastValid = (l == countriesByISO2.cend()) ? 0 : (*l);
const CountryInfo *lastValid = (l == _countriesByISO2.cend()) ? 0 : (*l);
for (int i = 0; i < countriesCount; ++i) {
const CountryInfo *ins = lastValid ? (i ? (countries + i - (seenLastValid ? 0 : 1)) : lastValid) : (countries + i);
if (lastValid && i && ins == lastValid) {
@@ -496,8 +498,8 @@ void CountrySelect::paintEvent(QPaintEvent *e) {
// draw box title / text
p.setPen(st::black->p);
p.setFont(st::addContactTitleFont->f);
p.drawText(_innerLeft + st::addContactTitlePos.x(), _innerTop + st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_country_select));
p.setFont(st::boxTitleFont->f);
p.drawText(_innerLeft + st::boxTitlePos.x(), _innerTop + st::boxTitlePos.y() + st::boxTitleFont->ascent, lang(lng_country_select));
}
}
}
@@ -542,8 +544,8 @@ void CountrySelect::resizeEvent(QResizeEvent *e) {
if (height() != e->oldSize().height()) {
_innerTop = st::introSelectDelta;
_innerHeight = height() - _innerTop - st::introSelectDelta;
if (_innerHeight > st::introSelectMaxHeight) {
_innerHeight = st::introSelectMaxHeight;
if (_innerHeight > st::boxMaxListHeight) {
_innerHeight = st::boxMaxListHeight;
_innerTop = (height() - _innerHeight) / 2;
}
}

View File

@@ -40,7 +40,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;

View File

@@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "gui/filedialog.h"
#include "application.h"
#include "localstorage.h"
void filedialogInit() {
if (cDialogLastPath().isEmpty()) {
@@ -67,13 +68,22 @@ void filedialogInit() {
// multipleFiles: 1 - multi open, 0 - single open, -1 - single save, -2 - select dir
bool _filedialogGetFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, int multipleFiles, QString startFile = QString()) {
filedialogInit();
#if defined Q_OS_LINUX || defined Q_OS_MAC // use native
remoteContent = QByteArray();
if (startFile.isEmpty() || startFile.at(0) != '/') {
startFile = cDialogLastPath() + '/' + startFile;
}
QString file;
if (multipleFiles >= 0) {
files = QFileDialog::getOpenFileNames(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, startFile, filter);
QString path = files.isEmpty() ? QString() : QFileInfo(files.back()).absoluteDir().absolutePath();
if (!path.isEmpty()) cSetDialogLastPath(path);
if (!path.isEmpty() && path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeUserSettings();
}
return !files.isEmpty();
} else if (multipleFiles < -1) {
file = QFileDialog::getExistingDirectory(App::wnd() ? App::wnd()->filedialogParent() : 0, caption);
@@ -87,14 +97,15 @@ bool _filedialogGetFiles(QStringList &files, QByteArray &remoteContent, const QS
return false;
} else {
QString path = QFileInfo(file).absoluteDir().absolutePath();
if (!path.isEmpty()) cSetDialogLastPath(path);
if (!path.isEmpty() && path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeUserSettings();
}
files = QStringList(file);
return true;
}
#endif
filedialogInit();
// hack for fast non-native dialog create
QFileDialog dialog(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, cDialogHelperPathFinal(), filter);
@@ -130,7 +141,11 @@ bool _filedialogGetFiles(QStringList &files, QByteArray &remoteContent, const QS
int res = dialog.exec();
cSetDialogLastPath(dialog.directory().absolutePath());
QString path = dialog.directory().absolutePath();
if (path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeUserSettings();
}
if (res == QDialog::Accepted) {
if (multipleFiles > 0) {

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

@@ -180,6 +180,85 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
return 0;
}
void FlatTextarea::getMentionHashtagStart(QString &start) const {
int32 pos = textCursor().position();
if (textCursor().anchor() != pos) return;
QTextDocument *doc(document());
QTextBlock block = doc->findBlock(pos);
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) {
QTextFragment fr(iter.fragment());
if (!fr.isValid()) continue;
int32 p = fr.position(), e = (p + fr.length());
if (p >= pos || e < pos) continue;
QTextCharFormat f = fr.charFormat();
if (f.isImageFormat()) continue;
QString t(fr.text());
for (int i = pos - p; i > 0; --i) {
if (t.at(i - 1) == '@') {
if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
start = t.mid(i - 1, pos - p - i + 1);
}
return;
} else if (t.at(i - 1) == '#') {
if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) {
start = t.mid(i - 1, pos - p - i + 1);
}
return;
}
if (pos - p - i > 63) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
}
return;
}
}
void FlatTextarea::onMentionOrHashtagInsert(QString mentionOrHashtag) {
QTextCursor c(textCursor());
int32 pos = c.position();
QTextDocument *doc(document());
QTextBlock block = doc->findBlock(pos);
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) {
QTextFragment fr(iter.fragment());
if (!fr.isValid()) continue;
int32 p = fr.position(), e = (p + fr.length());
if (p >= pos || e < pos) continue;
QTextCharFormat f = fr.charFormat();
if (f.isImageFormat()) continue;
QString t(fr.text());
for (int i = pos - p; i > 0; --i) {
if (t.at(i - 1) == '@' || t.at(i - 1) == '#') {
if ((i == pos - p || t.at(i).isLetter() || t.at(i - 1) == '#') && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
c.setPosition(p + i - 1, QTextCursor::MoveAnchor);
int till = p + i;
for (; (till < e) && (till - p - i + 1 < mentionOrHashtag.size()); ++till) {
if (t.at(till - p).toLower() != mentionOrHashtag.at(till - p - i + 1).toLower()) {
break;
}
}
if (till - p - i + 1 == mentionOrHashtag.size() && till < e && t.at(till - p) == ' ') {
++till;
}
c.setPosition(till, QTextCursor::KeepAnchor);
c.insertText(mentionOrHashtag + ' ');
return;
}
break;
}
if (pos - p - i > 63) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
}
}
c.insertText(mentionOrHashtag + ' ');
}
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
int32 end = textCursor().position(), start = end - 1;
if (textCursor().anchor() != end) return;
@@ -337,6 +416,106 @@ 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();
@@ -429,6 +608,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;
@@ -543,6 +738,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;
@@ -561,6 +763,7 @@ void FlatTextarea::keyPressEvent(QKeyEvent *e) {
}
}
}
if (spaceOrReturn) emit spacedReturnedPasted();
}
}

View File

@@ -27,7 +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;
bool viewportEvent(QEvent *e);
void touchEvent(QTouchEvent *e);
@@ -49,6 +48,7 @@ public:
QSize minimumSizeHint() const;
EmojiPtr getSingleEmoji() const;
void getMentionHashtagStart(QString &start) const;
void removeSingleEmoji();
QString getText(int32 start = 0, int32 end = -1) const;
bool hasText() const;
@@ -56,6 +56,11 @@ public:
bool isUndoAvailable() const;
bool isRedoAvailable() const;
void parseLinks();
QStringList linksList() const;
void insertFromMimeData(const QMimeData *source);
public slots:
void onTouchTimer();
@@ -66,12 +71,16 @@ public slots:
void onUndoAvailable(bool avail);
void onRedoAvailable(bool avail);
void onMentionOrHashtagInsert(QString mentionOrHashtag);
signals:
void changed();
void submitted(bool ctrlShiftEnter);
void cancelled();
void tabbed();
void spacedReturnedPasted();
void linksChanged();
protected:
@@ -103,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

@@ -51,7 +51,7 @@ const QPixmap &Image::pix(int32 w, int32 h) const {
checkload();
if (w <= 0 || !width() || !height()) {
w = width() * cIntRetinaFactor();
w = width();
} else if (cRetina()) {
w *= cIntRetinaFactor();
h *= cIntRetinaFactor();

View File

@@ -54,10 +54,10 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st)
}
void ScrollBar::recountSize() {
setGeometry(_vertical ? QRect(_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), 0, _st->width, _area->height()) : QRect(0, _area->height() - _st->width, _area->width(), _st->width));
}
void ScrollBar::updateBar() {
void ScrollBar::updateBar(bool force) {
QRect newBar;
if (_connected->maximum() != _scrollMax) {
int32 oldMax = _scrollMax, newMax = _connected->maximum();
@@ -68,8 +68,9 @@ void ScrollBar::updateBar() {
int sh = _area->scrollHeight(), rh = height() - 2 * _st->deltay, h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
if (h >= rh || !_area->scrollTopMax() || rh < _st->minHeight) {
if (!isHidden()) hide();
if (_topSh) emit topShadowVisibility(_topSh = (_st->topsh < 0));
if (_bottomSh) emit bottomShadowVisibility(_bottomSh = (_st->bottomsh < 0));
bool newTopSh = (_st->topsh < 0), newBottomSh = (_st->bottomsh < 0);
if (newTopSh != _topSh || force) emit topShadowVisibility(_topSh = newTopSh);
if (newBottomSh != _bottomSh || force) emit bottomShadowVisibility(_bottomSh = newBottomSh);
return;
}
@@ -97,8 +98,8 @@ void ScrollBar::updateBar() {
}
if (_vertical) {
bool newTopSh = (_st->topsh < 0) || (_area->scrollTop() > _st->topsh), newBottomSh = (_st->bottomsh < 0) || (_area->scrollTop() < _area->scrollTopMax() - _st->bottomsh);
if (newTopSh != _topSh) emit topShadowVisibility(_topSh = newTopSh);
if (newBottomSh != _bottomSh) emit bottomShadowVisibility(_bottomSh = newBottomSh);
if (newTopSh != _topSh || force) emit topShadowVisibility(_topSh = newTopSh);
if (newBottomSh != _bottomSh || force) emit bottomShadowVisibility(_bottomSh = newBottomSh);
}
if (isHidden()) show();
}
@@ -252,15 +253,16 @@ void ScrollBar::resizeEvent(QResizeEvent *e) {
}
ScrollArea::ScrollArea(QWidget *parent, const style::flatScroll &st, bool handleTouch) : QScrollArea(parent),
_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) {
_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()));
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()));
}

View File

@@ -70,7 +70,7 @@ public:
public slots:
void updateBar();
void updateBar(bool force = false);
void onHideTimer();
signals:

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,7 +45,7 @@ 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"
@@ -105,6 +118,7 @@ public:
return tmp;//_color;
}
virtual ITextBlock *clone() const = 0;
virtual ~ITextBlock() {
}
@@ -125,6 +139,10 @@ public:
return _nextDir;
}
ITextBlock *clone() const {
return new NewlineBlock(*this);
}
private:
NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, st::transparent, 0), _nextDir(Qt::LayoutDirectionAuto) {
@@ -160,6 +178,10 @@ public:
return _words.isEmpty() ? 0 : _words.back().f_rbearing();
}
ITextBlock *clone() const {
return new TextBlock(*this);
}
private:
TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex);
@@ -177,6 +199,10 @@ private:
class EmojiBlock : public ITextBlock {
public:
ITextBlock *clone() const {
return new EmojiBlock(*this);
}
private:
EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex, const EmojiData *emoji);
@@ -196,6 +222,10 @@ public:
return _height;
}
ITextBlock *clone() const {
return new SkipBlock(*this);
}
private:
SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex);
@@ -301,6 +331,32 @@ private:
};
class MentionLink : public ITextLink {
public:
MentionLink(const QString &tag) : _tag(tag) {
}
const QString &text() const {
return _tag;
}
void onClick(Qt::MouseButton button) const;
const QString &readable() const {
return _tag;
}
QString encoded() const {
return _tag;
}
private:
QString _tag;
};
class HashtagLink : public ITextLink {
public:
@@ -344,12 +400,6 @@ enum TextCommands {
TextCommandLangTag = 0x20,
};
enum {
TextParseMultiline = 0x01,
TextParseLinks = 0x02,
TextParseRichText = 0x04,
};
struct TextParseOptions {
int32 flags;
int32 maxw;
@@ -373,6 +423,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);
int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
@@ -391,13 +442,16 @@ public:
void replaceFont(style::font f); // does not recount anything, use at your own risk!
void draw(QPainter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, uint16 selectedFrom = 0, uint16 selectedTo = 0) const;
void drawElided(QPainter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1) const;
void drawElided(QPainter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0) const;
const TextLinkPtr &link(int32 x, int32 y, int32 width, style::align align = style::al_left) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, int32 width, style::align align = style::al_left) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y, int32 width, style::align align = style::al_left) const;
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType selectType) const;
bool isEmpty() const {
return _text.isEmpty();
}
QString original(uint16 selectedFrom = 0, uint16 selectedTo = 0xFFFF, bool expandLinks = true) const;
bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation
@@ -445,6 +499,13 @@ private:
};
void initLinkSets();
const QSet<int32> &validProtocols();
const QSet<int32> &validTopDomains();
const QRegularExpression &reDomain();
const QRegularExpression &reMailName();
const QRegularExpression &reHashtag();
// text style
const style::textStyle *textstyleCurrent();
void textstyleSet(const style::textStyle *style);
@@ -472,3 +533,111 @@ 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
return (ch >= 768 && ch < 880) || (ch >= 7616 && ch < 7680) || (ch >= 8400 && ch < 8448) || (ch >= 65056 && ch < 65072);
}
inline int32 chMaxDiacAfterSymbol() {
return 4;
}
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,18 +20,34 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "application.h"
namespace {
void _sendResizeEvents(QWidget *target) {
QResizeEvent e(target->size(), QSize());
QApplication::sendEvent(target, &e);
const QObjectList children = target->children();
for (int i = 0; i < children.size(); ++i) {
QWidget *child = static_cast<QWidget*>(children.at(i));
if (child->isWidgetType() && !child->isWindow() && child->testAttribute(Qt::WA_PendingResizeEvent)) {
_sendResizeEvents(child);
}
}
}
Qt::LayoutDirection _dir = Qt::LeftToRight;
bool _rtl = false;
void _sendResizeEvents(QWidget *target) {
QResizeEvent e(target->size(), QSize());
QApplication::sendEvent(target, &e);
const QObjectList children = target->children();
for (int i = 0; i < children.size(); ++i) {
QWidget *child = static_cast<QWidget*>(children.at(i));
if (child->isWidgetType() && !child->isWindow() && child->testAttribute(Qt::WA_PendingResizeEvent)) {
_sendResizeEvents(child);
}
}
}
}
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) {

View File

@@ -17,18 +17,53 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
class TWidget : public QWidget {
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 moveToRight(int x, int y, int w) {
move(rtl() ? x : (x + w - width()), y);
}
};
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) {
QFontMetrics m(fontMetrics());
if (rtl() && textWidth < 0) textWidth = m.width(text);
drawText(x + (rtl() ? (w - textWidth) : 0), y + m.ascent(), text);
}
void drawTextRight(int x, int y, int w, 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);
}
};
class TWidget : public Widget {
Q_OBJECT
public:
TWidget(QWidget *parent = 0) : QWidget(parent) {
TWidget(QWidget *parent = 0) : Widget(parent) {
}
TWidget *tparent() {
return dynamic_cast<TWidget*>(parentWidget());
return qobject_cast<TWidget*>(parentWidget());
}
const TWidget *tparent() const {
return dynamic_cast<const TWidget*>(parentWidget());
return qobject_cast<const TWidget*>(parentWidget());
}
virtual void leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -197,13 +197,14 @@ public:
HistoryHider(MainWidget *parent); // send path from command line argument
bool animStep(float64 ms);
bool withConfirm() const;
void paintEvent(QPaintEvent *e);
void keyPressEvent(QKeyEvent *e);
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void offerPeer(PeerId peer);
bool offerPeer(PeerId peer);
QString offeredText() const;
bool wasOffered() const;
@@ -217,6 +218,10 @@ public slots:
void startHide();
void forward();
signals:
void forwarded();
private:
void init();
@@ -252,6 +257,8 @@ public:
HistoryWidget(QWidget *parent);
void start();
void messagesReceived(const MTPmessages_Messages &messages, mtpRequestId requestId);
void windowShown();
@@ -269,6 +276,7 @@ public:
void contextMenuEvent(QContextMenuEvent *e);
void updateTopBarSelection();
void checkMentionDropdown();
void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth);
void topBarShadowParams(int32 &x, float64 &o);
@@ -281,7 +289,7 @@ public:
void peerMessagesUpdated();
void msgUpdated(PeerId peer, const HistoryItem *msg);
void newUnreadMsg(History *history, MsgId msgId);
void newUnreadMsg(History *history, HistoryItem *item);
void historyToDown(History *history);
void historyWasRead(bool force = true);
@@ -293,13 +301,13 @@ public:
void typingDone(const MTPBool &result, mtpRequestId req);
void destroyData();
void uploadImage(const QImage &img, bool withText = false);
void uploadImage(const QImage &img, bool withText = false, const QString &source = QString());
void uploadFile(const QString &file, bool withText = false); // with confirmation
void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText = false);
void uploadConfirmImageUncompressed(bool ctrlShiftEnter);
void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool withText = false);
void uploadConfirmImageUncompressed(bool ctrlShiftEnter, MsgId replyTo);
void uploadMedias(const QStringList &files, ToPrepareMediaType type);
void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer = 0);
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname);
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo);
void confirmSendImage(const ReadyLocalMedia &img);
void cancelSendImage();
@@ -308,11 +316,11 @@ public:
void updateOnlineDisplay(int32 x, int32 w);
void updateOnlineDisplayTimer();
mtpRequestId onForward(const PeerId &peer, SelectedItemSet toForward);
// mtpRequestId onForward(const PeerId &peer, SelectedItemSet toForward);
void onShareContact(const PeerId &peer, UserData *contact);
void onSendPaths(const PeerId &peer);
void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, int32 userId = 0);
void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId = 0);
PeerData *peer() const;
PeerData *activePeer() const;
@@ -339,10 +347,25 @@ public:
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void itemResized(HistoryItem *item);
void itemResized(HistoryItem *item, bool scrollToIt);
void updateScrollColors();
MsgId replyToId() const;
void updateReplyTo(bool force = false);
void cancelReply();
void updateForwarding(bool force = false);
void cancelForwarding(); // called by MainWidget
void clearReplyReturns();
void pushReplyReturn(HistoryItem *item);
QList<MsgId> replyReturns();
void setReplyReturns(PeerId peer, const QList<MsgId> &replyReturns);
void calcNextReplyReturn();
void updatePreview();
void previewCancel();
~HistoryWidget();
signals:
@@ -353,8 +376,15 @@ signals:
public slots:
void onCancel();
void onReplyToMessage();
void onReplyForwardPreviewCancel();
void onPreviewParse();
void onPreviewCheck();
void onPreviewTimeout();
void peerUpdated(PeerData *data);
void onPeerLoaded(PeerData *data);
void cancelTyping();
@@ -368,7 +398,7 @@ public slots:
void onListScroll();
void onHistoryToEnd();
void onSend(bool ctrlShiftEnter = false);
void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1);
void onPhotoSelect();
void onDocumentSelect();
@@ -376,6 +406,8 @@ public slots:
void onDocumentDrop(QDropEvent *e);
void onPhotoReady();
void onSendConfirmed();
void onSendCancelled();
void onPhotoFailed(quint64 id);
void showPeer(const PeerId &peer, MsgId msgId = 0, bool force = false, bool leaveActive = false);
void clearLoadingAround();
@@ -392,6 +424,7 @@ public slots:
void onFieldFocused();
void onFieldResize();
void onFieldCursorChanged();
void onScrollTimer();
void onForwardSelected();
@@ -409,11 +442,33 @@ public slots:
private:
MsgId _replyToId;
HistoryItem *_replyTo;
Text _replyToName, _replyToText;
int32 _replyToNameVersion;
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;
bool messagesFailed(const RPCError &error, mtpRequestId requestId);
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0);
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0, bool scrollToIt = false);
void addMessagesToFront(const QVector<MTPMessage> &messages);
void addMessagesToBack(const QVector<MTPMessage> &messages);
void chatLoaded(const MTPmessages_ChatFull &res);
void stickersGot(const MTPmessages_AllStickers &stickers);
bool stickersFailed(const RPCError &error);
@@ -421,7 +476,7 @@ private:
uint64 _lastStickersUpdate;
mtpRequestId _stickersUpdateRequest;
void writeDraft(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);
@@ -429,6 +484,7 @@ private:
void updateDragAreas();
bool _loadingMessages;
int32 histRequestsCount;
PeerData *histPeer;
History *_activeHist;
@@ -446,6 +502,8 @@ private:
IconedButton _toHistoryEnd;
MentionsDropdown _attachMention;
FlatButton _send;
IconedButton _attachDocument, _attachPhoto, _attachEmoji;
MessageField _field;
@@ -461,13 +519,11 @@ private:
LocalImageLoader imageLoader;
bool _synthedTextUpdate;
PeerId loadingChatId;
mtpRequestId loadingRequestId;
int64 serviceImageCacheSize;
QImage confirmImage;
PhotoId confirmImageId;
bool confirmWithText;
QString confirmSource;
QString titlePeerText;
int32 titlePeerTextWidth;

View File

@@ -26,6 +26,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "intro/introphone.h"
#include "intro/introcode.h"
#include "intro/introsignup.h"
#include "intro/intropwdcheck.h"
#include "mainwidget.h"
#include "window.h"
#include "application.h"
@@ -55,10 +56,13 @@ steps(new IntroSteps(this)),
phone(0),
code(0),
signup(0),
pwdcheck(0),
current(0),
moving(0),
visibilityChanging(0),
_callTimeout(60),
_registered(false),
_hasRecovery(false),
_back(this, st::setClose),
_backFrom(0), _backTo(0) {
setGeometry(QRect(0, st::titleHeight, wnd->width(), wnd->height() - st::titleHeight));
@@ -80,6 +84,8 @@ _backFrom(0), _backTo(0) {
show();
setFocus();
cSetPasswordRecovered(false);
_back.move(st::setClosePos.x(), st::setClosePos.y());
}
@@ -101,7 +107,7 @@ void IntroWidget::onParentResize(const QSize &newSize) {
void IntroWidget::onIntroBack() {
if (!current) return;
moving = -1;
moving = (current == 4) ? -2 : -1;
prepareMove();
}
@@ -117,7 +123,15 @@ bool IntroWidget::createNext() {
switch (current) {
case 0: stages[current + 1] = phone = new IntroPhone(this); break;
case 1: stages[current + 1] = code = new IntroCode(this); break;
case 2: stages[current + 1] = signup = new IntroSignup(this); break;
case 2:
if (_pwdSalt.isEmpty()) {
if (signup) delete signup;
stages[current + 1] = signup = new IntroSignup(this);
} else {
stages[current + 1] = pwdcheck = new IntroPwdCheck(this);
}
break;
case 3: stages[current + 1] = signup = new IntroSignup(this); break;
}
}
_back.raise();
@@ -126,11 +140,14 @@ bool IntroWidget::createNext() {
void IntroWidget::prepareMove() {
if (cacheForHide.isNull() || cacheForHideInd != current) makeHideCache();
stages[current + moving]->prepareShow();
if (cacheForShow.isNull() || cacheForShowInd != current + moving) makeShowCache();
xCoordHide = anim::ivalue(0, -moving * st::introSlideShift);
int32 m = (moving > 0) ? 1 : -1;
xCoordHide = anim::ivalue(0, -m * st::introSlideShift);
cAlphaHide = anim::fvalue(1, 0);
xCoordShow = anim::ivalue(moving * st::introSlideShift, 0);
xCoordShow = anim::ivalue(m * st::introSlideShift, 0);
cAlphaShow = anim::fvalue(0, 1);
anim::start(this);
@@ -154,7 +171,7 @@ void IntroWidget::onDoneStateChanged(int oldState, ButtonStateChangeSource sourc
} else {
makeHideCache();
}
} else if (source == ButtonByHover) {
} else if (source == ButtonByHover && current != 2) {
if (!createNext()) return;
if (!cacheForShow) makeShowCache(current + 1);
}
@@ -299,6 +316,23 @@ void IntroWidget::setCode(const QString &code) {
_code = code;
}
void IntroWidget::setPwdSalt(const QByteArray &salt) {
_pwdSalt = salt;
delete signup;
delete pwdcheck;
stages[3] = stages[4] = 0;
signup = 0;
pwdcheck = 0;
}
void IntroWidget::setHasRecovery(bool has) {
_hasRecovery = has;
}
void IntroWidget::setPwdHint(const QString &hint) {
_pwdHint = hint;
}
void IntroWidget::setCallTimeout(int32 callTimeout) {
_callTimeout = callTimeout;
}
@@ -319,12 +353,25 @@ int32 IntroWidget::getCallTimeout() const {
return _callTimeout;
}
const QByteArray &IntroWidget::getPwdSalt() const {
return _pwdSalt;
}
bool IntroWidget::getHasRecovery() const {
return _hasRecovery;
}
const QString &IntroWidget::getPwdHint() const {
return _pwdHint;
}
void IntroWidget::resizeEvent(QResizeEvent *e) {
QRect r(innerRect());
if (steps) steps->setGeometry(r);
if (phone) phone->setGeometry(r);
if (code) code->setGeometry(r);
if (signup) signup->setGeometry(r);
if (pwdcheck) pwdcheck->setGeometry(r);
}
void IntroWidget::mousePressEvent(QMouseEvent *e) {
@@ -355,6 +402,7 @@ void IntroWidget::rpcInvalidate() {
if (phone) phone->rpcInvalidate();
if (code) code->rpcInvalidate();
if (signup) signup->rpcInvalidate();
if (pwdcheck) pwdcheck->rpcInvalidate();
}
IntroWidget::~IntroWidget() {
@@ -362,5 +410,6 @@ IntroWidget::~IntroWidget() {
delete phone;
delete code;
delete signup;
delete pwdcheck;
if (App::wnd()) App::wnd()->noIntro(this);
}

View File

@@ -22,6 +22,7 @@ class IntroSteps;
class IntroPhone;
class IntroCode;
class IntroSignup;
class IntroPwdCheck;
class IntroStage;
class Text;
@@ -48,11 +49,17 @@ public:
void setPhone(const QString &phone, const QString &phone_hash, bool registered);
void setCode(const QString &code);
void setCallTimeout(int32 callTimeout);
void setPwdSalt(const QByteArray &salt);
void setHasRecovery(bool hasRecovery);
void setPwdHint(const QString &hint);
const QString &getPhone() const;
const QString &getPhoneHash() const;
const QString &getCode() const;
int32 getCallTimeout() const;
const QByteArray &getPwdSalt() const;
bool getHasRecovery() const;
const QString &getPwdHint() const;
void finish(const MTPUser &user, const QImage &photo = QImage());
@@ -96,7 +103,8 @@ private:
IntroPhone *phone;
IntroCode *code;
IntroSignup *signup;
IntroStage *stages[4];
IntroPwdCheck *pwdcheck;
IntroStage *stages[5];
int current, moving, visibilityChanging;
QString _phone, _phone_hash;
@@ -105,6 +113,10 @@ private:
QString _code;
QByteArray _pwdSalt;
bool _hasRecovery;
QString _pwdHint;
QString _firstname, _lastname;
IconedButton _back;
@@ -119,6 +131,8 @@ public:
}
virtual void activate() = 0; // show and activate
virtual void prepareShow() {
}
virtual void deactivate() = 0; // deactivate and hide
virtual void onNext() = 0;
virtual void onBack() = 0;

View File

@@ -158,6 +158,14 @@ void IntroCode::activate() {
code.setFocus();
}
void IntroCode::prepareShow() {
code.setText(QString());
if (sentRequest) {
MTP::cancel(sentRequest);
sentRequest = 0;
}
}
void IntroCode::deactivate() {
callTimer.stop();
hide();
@@ -216,8 +224,13 @@ bool IntroCode::codeSubmitFail(const RPCError &error) {
intro()->setCode(sentCode);
intro()->onIntroNext();
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (err == "SESSION_PASSWORD_NEEDED") {
intro()->setCode(sentCode);
code.setDisabled(false);
checkRequest.start(1000);
sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&IntroCode::gotPassword), rpcFail(&IntroCode::codeSubmitFail));
return true;
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
code.setFocus();
return true;
@@ -251,6 +264,24 @@ void IntroCode::callDone(const MTPBool &v) {
}
}
void IntroCode::gotPassword(const MTPaccount_Password &result) {
stopCheck();
code.setDisabled(false);
switch (result.type()) {
case mtpc_account_noPassword: // should not happen
code.setFocus();
break;
case mtpc_account_password: {
const MTPDaccount_password &d(result.c_account_password());
intro()->setPwdSalt(qba(d.vcurrent_salt));
intro()->setHasRecovery(d.vhas_recovery.v);
intro()->setPwdHint(qs(d.vhint));
intro()->onIntroNext();
} break;
}
}
void IntroCode::onSubmitCode(bool force) {
if (!force && (code.text() == sentCode || !code.isEnabled())) return;
@@ -262,6 +293,9 @@ void IntroCode::onSubmitCode(bool force) {
checkRequest.start(1000);
sentCode = code.text();
intro()->setPwdSalt(QByteArray());
intro()->setHasRecovery(false);
intro()->setPwdHint(QString());
sentRequest = MTP::send(MTPauth_SignIn(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(sentCode)), rpcDone(&IntroCode::codeSubmitDone), rpcFail(&IntroCode::codeSubmitFail));
}

View File

@@ -52,6 +52,7 @@ public:
bool animStep(float64 ms);
void activate();
void prepareShow();
void deactivate();
void onNext();
void onBack();
@@ -74,6 +75,8 @@ private:
void showError(const QString &err);
void callDone(const MTPBool &v);
void gotPassword(const MTPaccount_Password &result);
void stopCheck();
QString error;

View File

@@ -266,8 +266,7 @@ bool IntroPhone::phoneSubmitFail(const RPCError &error) {
showError(lang(lng_bad_phone));
enableAll(true);
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
enableAll(true);
return true;

View File

@@ -0,0 +1,371 @@
/*
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 "style.h"
#include "gui/filedialog.h"
#include "boxes/confirmbox.h"
#include "application.h"
#include "intro/intropwdcheck.h"
#include "intro/intro.h"
IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStage(parent),
errorAlpha(0),
_next(this, lang(lng_intro_submit), st::btnIntroNext),
_salt(parent->getPwdSalt()),
_hasRecovery(parent->getHasRecovery()),
_hint(parent->getPwdHint()),
_pwdField(this, st::inpIntroPassword, lang(lng_signin_password)),
_codeField(this, st::inpIntroPassword, lang(lng_signin_code)),
_toRecover(this, lang(lng_signin_recover)),
_toPassword(this, lang(lng_signin_try_password)),
_reset(this, lang(lng_signin_reset_account), st::btnRedLink),
sentRequest(0) {
setVisible(false);
setGeometry(parent->innerRect());
connect(&_next, SIGNAL(clicked()), this, SLOT(onSubmitPwd()));
connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
connect(&_toRecover, SIGNAL(clicked()), this, SLOT(onToRecover()));
connect(&_toPassword, SIGNAL(clicked()), this, SLOT(onToPassword()));
connect(&_pwdField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(&_codeField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(&_reset, SIGNAL(clicked()), this, SLOT(onReset()));
_pwdField.setEchoMode(QLineEdit::Password);
if (!_hint.isEmpty()) {
_hintText.setText(st::introFont, lng_signin_hint(lt_password_hint, _hint));
}
_codeField.hide();
_toPassword.hide();
_toRecover.show();
_reset.hide();
setMouseTracking(true);
}
void IntroPwdCheck::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(textRect, lang(lng_signin_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(textRect, lang(_pwdField.isHidden() ? lng_signin_recover_desc : lng_signin_desc), style::al_bottom);
}
if (_pwdField.isHidden()) {
if (!_emailPattern.isEmpty()) {
p.drawText(QRect(textRect.x(), _pwdField.y() + _pwdField.height() + st::introFinishSkip, textRect.width(), st::introFont->height), _emailPattern, style::al_top);
}
} else if (!_hint.isEmpty()) {
_hintText.drawElided(p, _pwdField.x(), _pwdField.y() + _pwdField.height() + st::introFinishSkip, _pwdField.width(), 1, style::al_top);
}
if (animating() || error.length()) {
p.setOpacity(errorAlpha.current());
QRect errRect((width() - st::introErrWidth) / 2, (_pwdField.y() + _pwdField.height() + st::introFinishSkip + st::introFont->height + _next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight);
p.setFont(st::introErrFont->f);
p.setPen(st::introErrColor->p);
p.drawText(errRect, error, QTextOption(style::al_center));
p.setOpacity(1);
}
}
void IntroPwdCheck::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next.move((width() - _next.width()) / 2, st::introBtnTop);
_pwdField.move((width() - _pwdField.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_codeField.move((width() - _codeField.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_toRecover.move(_next.x() + (_pwdField.width() - _toRecover.width()) / 2, _next.y() + _next.height() + st::introFinishSkip);
_toPassword.move(_next.x() + (_pwdField.width() - _toPassword.width()) / 2, _next.y() + _next.height() + st::introFinishSkip);
_reset.move((width() - _reset.width()) / 2, _toRecover.y() + _toRecover.height() + st::introFinishSkip);
}
textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
}
void IntroPwdCheck::showError(const QString &err) {
if (!animating() && err == error) return;
if (err.length()) {
error = err;
errorAlpha.start(1);
} else {
errorAlpha.start(0);
}
anim::start(this);
}
bool IntroPwdCheck::animStep(float64 ms) {
float64 dt = ms / st::introErrDuration;
bool res = true;
if (dt >= 1) {
res = false;
errorAlpha.finish();
if (!errorAlpha.current()) {
error = "";
}
} else {
errorAlpha.update(dt, st::introErrFunc);
}
update();
return res;
}
void IntroPwdCheck::activate() {
show();
if (_pwdField.isHidden()) {
_codeField.setFocus();
} else {
_pwdField.setFocus();
}
}
void IntroPwdCheck::deactivate() {
hide();
}
void IntroPwdCheck::stopCheck() {
checkRequest.stop();
}
void IntroPwdCheck::onCheckRequest() {
int32 status = MTP::state(sentRequest);
if (status < 0) {
int32 leftms = -status;
if (leftms >= 1000) {
MTP::cancel(sentRequest);
sentRequest = 0;
if (!_pwdField.isEnabled()) {
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
activate();
}
}
}
if (!sentRequest && status == MTP::RequestSent) {
stopCheck();
}
}
void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
sentRequest = 0;
stopCheck();
if (recover) {
cSetPasswordRecovered(true);
}
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
const MTPDauth_authorization &d(result.c_auth_authorization());
if (d.vuser.type() != mtpc_userSelf) { // wtf?
showError(lang(lng_server_error));
return;
}
intro()->finish(d.vuser);
}
bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
sentRequest = 0;
stopCheck();
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
const QString &err = error.type();
if (err == "PASSWORD_HASH_INVALID") {
showError(lang(lng_signin_bad_password));
_pwdField.notaBene();
return true;
} else if (err == "PASSWORD_EMPTY") {
intro()->onIntroBack();
} else if (err.startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
_pwdField.notaBene();
return true;
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
} else {
showError(lang(lng_server_error));
}
_pwdField.setFocus();
return false;
}
bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
sentRequest = 0;
stopCheck();
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
const QString &err = error.type();
if (err == "PASSWORD_EMPTY") {
intro()->onIntroBack();
return true;
} else if (err == "PASSWORD_RECOVERY_NA") {
recoverStartFail(error);
return true;
} else if (err == "PASSWORD_RECOVERY_EXPIRED") {
_emailPattern = QString();
onToPassword();
return true;
} else if (err == "CODE_INVALID") {
showError(lang(lng_signin_wrong_code));
_codeField.notaBene();
return true;
} else if (err.startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
_codeField.notaBene();
return true;
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
} else {
showError(lang(lng_server_error));
}
_codeField.setFocus();
return false;
}
void IntroPwdCheck::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = st::introFont->m.elidedText(lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern)), Qt::ElideRight, textRect.width());
update();
}
bool IntroPwdCheck::recoverStartFail(const RPCError &error) {
stopCheck();
_pwdField.setDisabled(false);
_codeField.setDisabled(false);
_pwdField.show();
_codeField.hide();
_pwdField.setFocus();
update();
showError("");
return true;
}
void IntroPwdCheck::onToRecover() {
if (_hasRecovery) {
if (sentRequest) {
MTP::cancel(sentRequest);
sentRequest = 0;
}
showError("");
_toRecover.hide();
_toPassword.show();
_pwdField.hide();
_pwdField.setText(QString());
_codeField.show();
_codeField.setFocus();
if (_emailPattern.isEmpty()) {
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&IntroPwdCheck::recoverStarted), rpcFail(&IntroPwdCheck::recoverStartFail));
}
update();
} else {
ConfirmBox *box = new ConfirmBox(lang(lng_signin_no_email_forgot), true);
App::wnd()->showLayer(box);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset()));
}
}
void IntroPwdCheck::onToPassword() {
ConfirmBox *box = new ConfirmBox(lang(lng_signin_no_email_forgot), true);
App::wnd()->showLayer(box);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset()));
}
void IntroPwdCheck::onToReset() {
if (sentRequest) {
MTP::cancel(sentRequest);
sentRequest = 0;
}
_toRecover.show();
_toPassword.hide();
_pwdField.show();
_codeField.hide();
_codeField.setText(QString());
_pwdField.setFocus();
_reset.show();
update();
}
void IntroPwdCheck::onReset() {
if (sentRequest) return;
ConfirmBox *box = new ConfirmBox(lang(lng_sigin_sure_reset), lang(lng_sigin_reset), QString(), st::btnRedDone);
connect(box, SIGNAL(confirmed()), this, SLOT(onResetSure()));
App::wnd()->showLayer(box);
}
void IntroPwdCheck::onResetSure() {
if (sentRequest) return;
sentRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&IntroPwdCheck::deleteDone), rpcFail(&IntroPwdCheck::deleteFail));
}
bool IntroPwdCheck::deleteFail(const RPCError &error) {
if (mtpIsFlood(error)) return false;
sentRequest = 0;
showError(lang(lng_server_error));
return true;
}
void IntroPwdCheck::deleteDone(const MTPBool &v) {
App::wnd()->hideLayer();
intro()->onIntroNext();
}
void IntroPwdCheck::onInputChange() {
showError("");
}
void IntroPwdCheck::onSubmitPwd(bool force) {
if (sentRequest) return;
if (_pwdField.isHidden()) {
if (!force && !_codeField.isEnabled()) return;
QString code = _codeField.text().trimmed();
if (code.isEmpty()) {
_codeField.notaBene();
return;
}
sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&IntroPwdCheck::pwdSubmitDone, true), rpcFail(&IntroPwdCheck::codeSubmitFail));
} else {
if (!force && !_pwdField.isEnabled()) return;
_pwdField.setDisabled(true);
setFocus();
showError("");
QByteArray pwdData = _salt + _pwdField.text().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized);
hashSha256(pwdData.constData(), pwdData.size(), pwdHash.data());
sentRequest = MTP::send(MTPauth_CheckPassword(MTP_string(pwdHash)), rpcDone(&IntroPwdCheck::pwdSubmitDone, false), rpcFail(&IntroPwdCheck::pwdSubmitFail));
}
}
void IntroPwdCheck::onNext() {
onSubmitPwd();
}
void IntroPwdCheck::onBack() {
}

View File

@@ -0,0 +1,88 @@
/*
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 <QtWidgets/QWidget>
#include "gui/flatbutton.h"
#include "gui/flatinput.h"
#include "intro.h"
class IntroPwdCheck : public IntroStage, public Animated, public RPCSender {
Q_OBJECT
public:
IntroPwdCheck(IntroWidget *parent);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
bool animStep(float64 ms);
void activate();
void deactivate();
void onNext();
void onBack();
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
bool pwdSubmitFail(const RPCError &error);
bool codeSubmitFail(const RPCError &error);
bool recoverStartFail(const RPCError &error);
void recoverStarted(const MTPauth_PasswordRecovery &result);
public slots:
void onSubmitPwd(bool force = false);
void onToRecover();
void onToPassword();
void onInputChange();
void onCheckRequest();
void onToReset();
void onReset();
void onResetSure();
private:
void showError(const QString &err);
void stopCheck();
void deleteDone(const MTPBool &result);
bool deleteFail(const RPCError &error);
QString error;
anim::fvalue errorAlpha;
FlatButton _next;
QRect textRect;
QByteArray _salt;
bool _hasRecovery;
QString _hint, _emailPattern;
FlatInput _pwdField, _codeField;
LinkButton _toRecover, _toPassword, _reset;
mtpRequestId sentRequest;
Text _hintText;
QByteArray _pwdSalt;
QTimer checkRequest;
};

View File

@@ -103,7 +103,6 @@ void IntroSignup::paintEvent(QPaintEvent *e) {
p.setOpacity(errorAlpha.current());
QRect errRect((width() - st::introErrWidth) / 2, (last.y() + last.height() + next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight);
p.fillRect(errRect, st::introErrBG->b);
p.setFont(st::introErrFont->f);
p.setPen(st::introErrColor->p);
p.drawText(errRect, error, QTextOption(style::al_center));
@@ -240,8 +239,7 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) {
showError(lang(lng_bad_name));
last.setFocus();
return true;
}
if (QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err).hasMatch()) {
} else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) {
showError(lang(lng_flood_error));
last.setFocus();
return true;

View File

@@ -19,10 +19,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "lang.h"
Qt::LayoutDirection langDir() { // current lang dependent
return Qt::LeftToRight;
}
LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang dependent
int v = qFloor(value);
QString sv;

View File

@@ -92,8 +92,6 @@ inline LangString langDayOfWeekFull(const QDate &date) {
return (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1_full + day - 1)) : qsl("DAY_ERR");
}
Qt::LayoutDirection langDir();
class LangLoader {
public:
const QString &errors() const;

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";
@@ -91,12 +91,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_server_error" = "Interner Serverfehler.";
"lng_flood_error" = "Zu viele Versuche, bitte später erneut probieren.";
"lng_deleted" = "Gelöschter Kontakt";
"lng_deleted_message" = "Gelöschte Nachricht";
"lng_intro" = "Willkommen beim [a href=\"https://telegram.org/\"]offiziellen Desktop Messenger[/a].\n[b]Schnell[/b] und [b]sicher[/b] chatten leicht gemacht.";
"lng_start_msgs" = "JETZT STARTEN";
"lng_intro_next" = "WEITER";
"lng_intro_finish" = "REGISTRIEREN";
"lng_intro_submit" = "SENDEN";
"lng_phone_ph" = "Deine Telefonnummer";
"lng_phone_title" = "Dein Telefon";
@@ -123,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.";
@@ -207,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.";
@@ -228,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";
@@ -239,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)";
@@ -263,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";
@@ -274,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";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Weiter";
"lng_create_group_title" = "Neue Gruppe erstellen";
"lng_failed_add_participant" = "Kann Teilnehmer nicht hinzufügen. Später erneut versuchen.";
"lng_sure_delete_contact" = "Bist du sicher, dass du {contact} von deinen Kontakten löschen willst?";
"lng_sure_delete_history" = "Sicher, dass du den kompletten Verlauf mit {contact} löschen willst?\n\nDas kann man nicht rückgängig machen.";
"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";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} hat die Gruppe «{title}» erstellt";
"lng_forwarded_from" = "Weitergeleitet von";
"lng_in_reply_to" = "Antwort auf";
"lng_attach_failed" = "Uploadfehler";
"lng_attach_file" = "Datei";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "E-Mail-Adresse kopieren";
"lng_context_open_hashtag" = "Nach Hashtag suchen";
"lng_context_copy_hashtag" = "Hashtag kopieren";
"lng_context_open_mention" = "Profil öffnen";
"lng_context_copy_mention" = "Benutzername kopieren";
"lng_context_open_image" = "Bild öffnen";
"lng_context_save_image" = "Bild speichern unter";
"lng_context_forward_image" = "Bild weiterleiten";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Datei weiterleiten";
"lng_context_delete_file" = "Datei löschen";
"lng_context_close_file" = "Datei schließen";
"lng_context_copy_text" = "Nachricht kopieren";
"lng_context_copy_text" = "Text kopieren";
"lng_context_to_msg" = "Zur Nachricht";
"lng_context_reply_msg" = "Antworten";
"lng_context_forward_msg" = "Nachricht weiterleiten";
"lng_context_delete_msg" = "Nachricht löschen";
"lng_context_select_msg" = "Nachricht auswählen";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "Ausgewählte Dateien an {recipient} senden?";
"lng_forward" = "Weiterleiten";
"lng_forward_send" = "Senden";
"lng_forward_messages" = "{count:_not_used_|Nachrichtenanhang|# Nachrichtenanhänge}";
"lng_forwarding_from" = "{user} und {count:_not_used_|# anderer|# andere}";
"lng_forwarding_from_two" = "{user} und {second_user}";
"lng_contact_phone" = "Telefonnummer";
"lng_enter_contact_data" = "Neuer Kontakt";
@@ -477,12 +541,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_version7020" = "— Sperre deine App mit einem Pincode";
"lng_new_version7020_appstore" = "— Sperre deine App mit einem Pincode\n— Ändere das Chat-Hintergrundbild\n— Neues <<öffnen mit>> Menü für Dateien\n— Koreanische Sprache hinzugefügt";
"lng_new_version_text" = "— Linkvorschau: Erhalte eine Zusammenfassung für Tweets, YouTube Videos, Instagram Fotos und sonstigen Inhalten.\n— Zweistufige Bestätigung: Lege ein zusätzliches Kennwort fest, das du für die Anmeldung bei deinem Telegram-Konto benötigst.\n— Sitzungslisten: Betrachte alle deine aktiven Telegram-Sitzungen (auf dem Desktop, Tablet und mobilen Geräten) und beende sie.";
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";

View File

@@ -89,14 +89,16 @@ 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";
"lng_intro" = "La app oficial para PC de [a href=\"https://telegram.org/\"]Telegram[/a].\nEs [b]rápida[/b] y [b]segura[/b].";
"lng_start_msgs" = "EMPEZAR A CONVERSAR";
"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";
@@ -123,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.";
@@ -228,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:";
@@ -239,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_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";
@@ -249,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" = "Pon, otra vez, la 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)";
@@ -263,8 +310,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_connection_user_ph" = "Apodo";
"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";
@@ -274,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";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Siguiente";
"lng_create_group_title" = "Nuevo grupo";
"lng_failed_add_participant" = "No se pudo añadir el usuario. Por favor, inténtalo más tarde.";
"lng_sure_delete_contact" = "¿Quieres eliminar a {contact} de tu lista de contactos?";
"lng_sure_delete_history" = "¿Quieres eliminar todo el historial de mensajes con {contact}?\n\nEsta acción no se puede deshacer.";
"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}";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} creó el grupo «{title}»";
"lng_forwarded_from" = "Reenviado desde";
"lng_in_reply_to" = "Respondiendo a";
"lng_attach_failed" = "Fallido";
"lng_attach_file" = "Archivo";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "Copiar dirección de correo electrónico";
"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_open_image" = "Abrir imagen";
"lng_context_save_image" = "Guardar como...";
"lng_context_forward_image" = "Reenviar imagen";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Reenviar archivo";
"lng_context_delete_file" = "Eliminar archivo";
"lng_context_close_file" = "Cerrar archivo";
"lng_context_copy_text" = "Copiar el texto";
"lng_context_copy_text" = "Copiar texto";
"lng_context_to_msg" = "Ir al mensaje";
"lng_context_reply_msg" = "Responder";
"lng_context_forward_msg" = "Reenviar mensaje";
"lng_context_delete_msg" = "Eliminar mensaje";
"lng_context_select_msg" = "Elegir mensaje";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "¿Enviar los archivos elegidos a {recipient}?";
"lng_forward" = "Reenviar";
"lng_forward_send" = "Enviar";
"lng_forward_messages" = "{count:_not_used_|Mensaje adjunto|# mensajes adjuntos}";
"lng_forwarding_from" = "{user} y {count:_not_used_|# otro|# otros}";
"lng_forwarding_from_two" = "{user} y {second_user}";
"lng_contact_phone" = "Número de teléfono";
"lng_enter_contact_data" = "Nuevo contacto";
@@ -477,12 +541,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_version7020" = "- Bloquea tu aplicación con un código de acceso";
"lng_new_version7020_appstore" = "— Bloquea tu aplicación con un código de acceso\n— Cambia el fondo de chat\n— Nuevo menú «abrir con» para archivos\n— Idioma coreano añadido";
"lng_new_version_text" = "— Vista previa de enlaces de Twitter, YouTube, Instagram y otros\n— Verificación en dos pasos\n— Ve todas tus sesiones de Telegram y cierra sesiones específicas";
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";

View File

@@ -21,7 +21,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_menu_contacts" = "Contatti";
"lng_menu_settings" = "Impostazioni";
"lng_menu_about" = "Info";
"lng_menu_update" = "Aggiornamento";
"lng_menu_update" = "Aggiorna";
"lng_menu_restart" = "Riavvia";
"lng_menu_back" = "Indietro";
@@ -91,12 +91,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_server_error" = "Errore interno del server.";
"lng_flood_error" = "Troppi tentativi. Per favore riprova più tardi.";
"lng_deleted" = "Sconosciuto";
"lng_deleted_message" = "Messaggio eliminato";
"lng_intro" = "Benvenuti nell'app desktop ufficiale di [a href=\"https://telegram.org/\"]Telegram[/a].\nÈ [b]veloce[/b] e [b]sicura[/b].";
"lng_start_msgs" = "INIZIA A MESSAGGIARE";
"lng_intro_next" = "AVANTI";
"lng_intro_finish" = "ISCRIVITI";
"lng_intro_submit" = "INVIA";
"lng_phone_ph" = "Tuo numero di telefono";
"lng_phone_title" = "Tuo telefono";
@@ -123,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.";
@@ -188,9 +208,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_replace_emojis" = "Riconosci emoji";
"lng_settings_view_emojis" = "Vedi la lista";
"lng_settings_emoji_list" = "Lista delle emoji supportate";
"lng_settings_send_enter" = "Invia con Invio";
"lng_settings_send_ctrlenter" = "Invia con Ctrl+Invio";
"lng_settings_send_cmdenter" = "Invia con Cmd+Invio";
"lng_settings_send_enter" = "Spedisci con Invio";
"lng_settings_send_ctrlenter" = "Spedisci con Ctrl+Invio";
"lng_settings_send_cmdenter" = "Spedisci con Cmd+Invio";
"lng_settings_section_background" = "Sfondo chat";
"lng_settings_bg_from_gallery" = "Scegli dalla galleria";
@@ -228,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";
@@ -239,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)";
@@ -263,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";
@@ -274,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";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Avanti";
"lng_create_group_title" = "Nuovo gruppo";
"lng_failed_add_participant" = "Impossibile aggiungere l'utente. Riprova più tardi.";
"lng_sure_delete_contact" = "Sicuro di volere eliminare {contact} dalla tua lista dei contatti?";
"lng_sure_delete_history" = "Sicuro di voler eliminare tutta la cronologia dei messaggi con {contact}?\n\nQuesta azione non può essere annullata.";
"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}";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} ha creato il gruppo «{title}»";
"lng_forwarded_from" = "Inoltrato da";
"lng_in_reply_to" = "In risposta a";
"lng_attach_failed" = "Fallito";
"lng_attach_file" = "File";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "Copia indirizzo email";
"lng_context_open_hashtag" = "Cerca via hashtag";
"lng_context_copy_hashtag" = "Copia hashtag";
"lng_context_open_mention" = "Apri profilo";
"lng_context_copy_mention" = "Copia username";
"lng_context_open_image" = "Apri immagine";
"lng_context_save_image" = "Salva immagine come..";
"lng_context_forward_image" = "Inoltra immagine";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Inoltra file";
"lng_context_delete_file" = "Elimina file";
"lng_context_close_file" = "Chiudi file";
"lng_context_copy_text" = "Copia testo messaggio";
"lng_context_copy_text" = "Copia testo";
"lng_context_to_msg" = "Vai al messaggio";
"lng_context_reply_msg" = "Rispondi";
"lng_context_forward_msg" = "Inoltra messaggio";
"lng_context_delete_msg" = "Elimina messaggio";
"lng_context_select_msg" = "Seleziona messaggio";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "Invia i file selezionati a {recipient}?";
"lng_forward" = "Inoltra";
"lng_forward_send" = "Invia";
"lng_forward_messages" = "{count:_not_used_|messaggio inoltrato|# messaggi inoltrati}";
"lng_forwarding_from" = "{user} e {count:_not_used_|# altro|altri #}";
"lng_forwarding_from_two" = "{user} e {second_user}";
"lng_contact_phone" = "Numero di telefono";
"lng_enter_contact_data" = "Nuovo contatto";
@@ -477,12 +541,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 tutte le 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_version7020" = "— Blocca la tua app con un codice";
"lng_new_version7020_appstore" = "— Blocca la tua app con un codice\n— Cambia lo sfondo chat\n— Nuovo menu «apri con» per i file\n— Aggiunto il Coreano";
"lng_new_version_text" = "— Anteprima dei link per Twitter, Youtube, Instagram e altri contenuti.\n— Verifica in due passaggi.\n— Visualizza le sessioni attive di Telegram e termina sessioni specifiche.";
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";

View File

@@ -59,44 +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" = "전화번호";
@@ -111,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" = "텔레그램이 회원님의 전화번호로 전화를 걸었습니다.";
@@ -123,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사진을 업로드해주세요.";
@@ -133,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" = "프로필 이미지 선택";
@@ -145,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" = "시스템 시작시 자동실행";
@@ -186,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로 메시지 전송";
@@ -218,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" = "초기화 완료!";
@@ -228,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" = "로그아웃";
@@ -274,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" = "연락처 추가";
@@ -295,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" = "전화번호 복사";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "다음";
"lng_create_group_title" = "새로운 그룹";
"lng_failed_add_participant" = "사용자를 초대할 수 없습니다. 나중에 다시 시도해주세요.";
"lng_sure_delete_contact" = "{contact} 님을 주소록에서 \n삭제하시겠습니까?";
"lng_sure_delete_history" = "{contact} 님과 관련된 모든 메시지를 \n삭제하시겠습니까?\n\n삭제 하실 경우 취소가 불가능합니다.";
"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} 님을 초대하셨습니다.";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} 님이 그룹 «{title}» 을 생성하셨습니다.";
"lng_forwarded_from" = "전달받음";
"lng_in_reply_to" = "다음 유저에게 답장 :";
"lng_attach_failed" = "실패";
"lng_attach_file" = "파일";
@@ -367,8 +425,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" = "사진 저장";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "이메일 복사";
"lng_context_open_hashtag" = "해시태그로 검색";
"lng_context_copy_hashtag" = "해시태그 복사";
"lng_context_open_mention" = "프로필 열기";
"lng_context_copy_mention" = "아이디 복사";
"lng_context_open_image" = "이미지 열기";
"lng_context_save_image" = "이미지를 다른 이름으로 저장..";
"lng_context_forward_image" = "이미지 전달";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "파일 전달";
"lng_context_delete_file" = "파일 삭제";
"lng_context_close_file" = "파일 닫기";
"lng_context_copy_text" = "메시지 내용 복사";
"lng_context_copy_text" = "텍스트 복사";
"lng_context_to_msg" = "메시지로 이동";
"lng_context_reply_msg" = "답장";
"lng_context_forward_msg" = "메시지 전달";
"lng_context_delete_msg" = "메시지 삭제";
"lng_context_select_msg" = "메시지 선택";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "선택한 파일을 {recipient} 님에게 보내시겠습니까?";
"lng_forward" = "전달";
"lng_forward_send" = "보낵";
"lng_forward_messages" = "{count:_not_used_|전달받은 메시지|# 개의 전달받은 메시지}";
"lng_forwarding_from" = "{user} 님과 {count:_not_used_|# 명|# 명}";
"lng_forwarding_from_two" = "{user} 님과 {second_user}";
"lng_contact_phone" = "전화번호";
"lng_enter_contact_data" = "새로운 연락처";
@@ -477,12 +541,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_version7020" = "— 잠금번호로 앱 잠금";
"lng_new_version7020_appstore" = "— 잠금번호로 앱 잠금\n— 채팅배경 변경\n— 파일을 «다음으로 열기» 설정\n— 한국어 추가";
"lng_new_version_text" = "— 트윗, 유투브 비디오, 인스터그램 사진등의 프리뷰 기능\n— 2단계 인증\n— 활성화된 모든 텔레그램 세션 확인 및 특정 세션 종료 기능";
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";

View File

@@ -91,12 +91,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_server_error" = "Interne serverfout.";
"lng_flood_error" = "Teveel pogingen. Probeer het later opnieuw.";
"lng_deleted" = "Onbekend";
"lng_deleted_message" = "Verwijderd bericht";
"lng_intro" = "Welkom bij de officiële [a href=\"https://telegram.org/\"]Telegram[/a] desktop-app.\n[b]Snel[/b] en [b]veilig[/b].";
"lng_start_msgs" = "BEGIN MET CHATTEN";
"lng_intro_next" = "VOLGENDE";
"lng_intro_finish" = "AANMELDEN";
"lng_intro_submit" = "VERSTUREN";
"lng_phone_ph" = "Je telefoonnummer";
"lng_phone_title" = "Je telefoon";
@@ -123,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.";
@@ -228,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";
@@ -239,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, klikerop 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)";
@@ -263,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";
@@ -274,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";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Volgende";
"lng_create_group_title" = "Nieuwe groep";
"lng_failed_add_participant" = "Gebruiker toevoegen mislukt. Probeer het later nog eens.";
"lng_sure_delete_contact" = "{contact} echt verwijderen uit contacten?";
"lng_sure_delete_history" = "Geschiedenis met {contact} echt wissen? \n\nDeze actie kan niet ongedaan worden gemaakt.";
"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";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} heeft de groep «{title}» gemaakt";
"lng_forwarded_from" = "Doorgestuurd van";
"lng_in_reply_to" = "Antwoord op";
"lng_attach_failed" = "Mislukt";
"lng_attach_file" = "Bestand";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "E-mailadres kopiëren";
"lng_context_open_hashtag" = "Zoeken op hashtag";
"lng_context_copy_hashtag" = "Hashtag kopiëren";
"lng_context_open_mention" = "Profiel openen";
"lng_context_copy_mention" = "Gebruikersnaam kopiëren";
"lng_context_open_image" = "Afbeelding openen";
"lng_context_save_image" = "Afbeelding opslaan als";
"lng_context_forward_image" = "Afbeelding doorsturen";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Bestand doorsturen";
"lng_context_delete_file" = "Bestand verwijderen";
"lng_context_close_file" = "Bestand sluiten";
"lng_context_copy_text" = "Bericht kopiëren";
"lng_context_copy_text" = "Tekst kopiëren";
"lng_context_to_msg" = "Naar bericht gaan";
"lng_context_reply_msg" = "Antwoord";
"lng_context_forward_msg" = "Bericht doorsturen";
"lng_context_delete_msg" = "Bericht verwijderen";
"lng_context_select_msg" = "Bericht kiezen";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "Gekozen bestanden naar {recipient} sturen?";
"lng_forward" = "Doorsturen";
"lng_forward_send" = "Stuur";
"lng_forward_messages" = "{count:_not_used_|Doorgestuurd bericht|# doorgestuurde berichten}";
"lng_forwarding_from" = "{user} en {count:_not_used_|# andere|# anderen}";
"lng_forwarding_from_two" = "{user} en {second_user}";
"lng_contact_phone" = "Telefoonnummer";
"lng_enter_contact_data" = "Nieuw contact";
@@ -477,12 +541,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_version7020" = "— Vergrendel je app met een toegangscode";
"lng_new_version7020_appstore" = "— Vergrendel je app met een toegangscode\n— Pas nu de chatachtergrond aan\n— Nieuw «openen met» menu voor bestanden\n— Koreaans toegevoegd";
"lng_new_version_text" = "— Link-preview voor Twitter, YouTube, Instagram en diverse andere links\n— Twee-staps-verificatie\n— Bekijk al je Telegram-sessies, beëindig specifieke sessies.";
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";

View File

@@ -91,12 +91,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_server_error" = "Erro interno do servidor.";
"lng_flood_error" = "Muitas tentativas. Por favor, tente novamente mais tarde.";
"lng_deleted" = "Desconhecido";
"lng_deleted_message" = "Mensagem apagada";
"lng_intro" = "Bem vindo ao cliente oficial do [a href=\"https://telegram.org/\"]Telegram[/a].\nÉ [b]rápido[/b] e [b]seguro[/b].";
"lng_start_msgs" = "COMECE A CONVERSAR";
"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";
@@ -123,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.";
@@ -228,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:";
@@ -239,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)";
@@ -263,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";
@@ -274,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";
@@ -312,13 +369,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_create_group_next" = "Próximo";
"lng_create_group_title" = "Novo Grupo";
"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_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_message_empty" = "(vazio)";
"lng_action_add_user" = "{from} adicionou {user}";
@@ -332,6 +389,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_created_chat" = "{from} criou o grupo «{title}»";
"lng_forwarded_from" = "Encaminhado de";
"lng_in_reply_to" = "Em resposta a";
"lng_attach_failed" = "Falhou";
"lng_attach_file" = "Arquivo";
@@ -385,6 +443,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "Copiar endereço de email";
"lng_context_open_hashtag" = "Buscar pela hashtag";
"lng_context_copy_hashtag" = "Copiar hashtag";
"lng_context_open_mention" = "Abrir perfil";
"lng_context_copy_mention" = "Copiar nome de usuário";
"lng_context_open_image" = "Abrir Imagem";
"lng_context_save_image" = "Salvar Imagem Como..";
"lng_context_forward_image" = "Encaminhar Imagem";
@@ -403,8 +463,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_forward_file" = "Encaminhar Arquivo";
"lng_context_delete_file" = "Apagar Arquivo";
"lng_context_close_file" = "Fechar Arquivo";
"lng_context_copy_text" = "Copiar Texto da Mensagem";
"lng_context_copy_text" = "Copiar Texto";
"lng_context_to_msg" = "Ir Para Mensagem";
"lng_context_reply_msg" = "Responder";
"lng_context_forward_msg" = "Encaminhar Mensagem";
"lng_context_delete_msg" = "Apagar Mensagem";
"lng_context_select_msg" = "Selecionar Mensagem";
@@ -425,6 +486,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_forward_send_files_confirm" = "Enviar arquivos selecionados para {recipient}?";
"lng_forward" = "Encaminhar";
"lng_forward_send" = "Enviar";
"lng_forward_messages" = "{count:_not_used_|Mensagem encaminhada|# mensagens encaminhadas}";
"lng_forwarding_from" = "{user} e {count:_not_used_|# outro|# outros}";
"lng_forwarding_from_two" = "{user} e {second_user}";
"lng_contact_phone" = "Número de telefone";
"lng_enter_contact_data" = "Novo Contato";
@@ -477,12 +541,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_version7020" = "— Bloquear o aplicativo com senha";
"lng_new_version7020_appstore" = "— Bloquear o aplicativo com senha\n— Alterar o plano de fundo \n— Novo menu «abrir com» para arquivos\n— Adicionada linguagem coreana";
"lng_new_version_text" = "— Pré-visualização de links para o Twitter, YouTube, Instagram e alguns outros links\n— Verificação em duas etapas\n— Visualizar todas as suas sessões do Telegram, encerrar sessões específicas.";
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";

View File

@@ -41,6 +41,7 @@ void LocalImageLoaderPrivate::prepareImages() {
ToPrepareMediaType type;
bool animated = false;
bool ctrlShiftEnter = false;
MsgId replyTo = 0;
{
QMutexLocker lock(loader->toPrepareMutex());
ToPrepareMedias &list(loader->toPrepareMedias());
@@ -53,6 +54,7 @@ void LocalImageLoaderPrivate::prepareImages() {
id = list.front().id;
type = list.front().type;
ctrlShiftEnter = list.front().ctrlShiftEnter;
replyTo = list.front().replyTo;
}
if (img.isNull()) {
@@ -186,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());
attributes.push_back(MTP_documentAttributeSticker(MTP_string("")));
thumbFormat = "webp";
thumbExt = qsl("webp");
}
@@ -212,7 +214,7 @@ void LocalImageLoaderPrivate::prepareImages() {
{
QMutexLocker lock(loader->readyMutex());
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter));
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
}
{
@@ -234,11 +236,11 @@ LocalImageLoaderPrivate::~LocalImageLoaderPrivate() {
LocalImageLoader::LocalImageLoader(QObject *parent) : QObject(parent), thread(0), priv(0) {
}
void LocalImageLoader::append(const QStringList &files, const PeerId &peer, ToPrepareMediaType t) {
void LocalImageLoader::append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
{
QMutexLocker lock(toPrepareMutex());
for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) {
toPrepare.push_back(ToPrepareMedia(*i, peer, t, false));
toPrepare.push_back(ToPrepareMedia(*i, peer, t, false, replyTo));
}
}
if (!thread) {
@@ -249,11 +251,11 @@ void LocalImageLoader::append(const QStringList &files, const PeerId &peer, ToPr
emit needToPrepare();
}
PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPrepareMediaType t) {
PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
PhotoId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(img, peer, t, false));
toPrepare.push_back(ToPrepareMedia(img, peer, t, false, replyTo));
result = toPrepare.back().id;
}
if (!thread) {
@@ -265,11 +267,11 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPr
return result;
}
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) {
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter) {
PhotoId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter));
toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter, replyTo));
result = toPrepare.back().id;
}
if (!thread) {
@@ -281,11 +283,11 @@ PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepar
return result;
}
PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, ToPrepareMediaType t) {
PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
PhotoId result = 0;
{
QMutexLocker lock(toPrepareMutex());
toPrepare.push_back(ToPrepareMedia(file, peer, t, false));
toPrepare.push_back(ToPrepareMedia(file, peer, t, false, replyTo));
result = toPrepare.back().id;
}
if (!thread) {

View File

@@ -25,11 +25,11 @@ enum ToPrepareMediaType {
};
struct ToPrepareMedia {
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) {
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) {
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) {
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
}
PhotoId id;
QString file;
@@ -38,13 +38,14 @@ struct ToPrepareMedia {
PeerId peer;
ToPrepareMediaType type;
bool ctrlShiftEnter;
MsgId replyTo;
};
typedef QList<ToPrepareMedia> ToPrepareMedias;
typedef QMap<int32, QByteArray> LocalFileParts;
struct ReadyLocalMedia {
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter) :
type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
if (!jpeg.isEmpty()) {
int32 size = jpeg.size();
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
@@ -54,6 +55,7 @@ struct ReadyLocalMedia {
hashMd5Hex(jpeg.constData(), jpeg.size(), jpeg_md5.data());
}
}
MsgId replyTo;
ToPrepareMediaType type;
QString file, filename;
int32 filesize;
@@ -103,10 +105,10 @@ class LocalImageLoader : public QObject {
public:
LocalImageLoader(QObject *parent);
void append(const QStringList &files, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto);
PhotoId append(const QByteArray &img, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto);
PhotoId append(const QImage &img, const PeerId &peer, ToPrepareMediaType t = ToPreparePhoto, bool ctrlShiftEnter = false);
PhotoId append(const QString &file, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto);
void append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
PhotoId append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter = false);
PhotoId append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
QMutex *readyMutex();
ReadyLocalMedias &readyList();

View File

@@ -494,6 +494,7 @@ namespace {
lskRecentStickers, // no data
lskBackground, // no data
lskUserSettings, // no data
lskRecentHashtags, // no data
};
typedef QMap<PeerId, FileKey> DraftsMap;
@@ -514,6 +515,8 @@ namespace {
bool _backgroundWasRead = false;
FileKey _userSettingsKey = 0;
FileKey _recentHashtagsKey = 0;
bool _recentHashtagsWereRead = false;
typedef QPair<FileKey, qint32> FileDesc; // file, size
typedef QMap<StorageKey, FileDesc> StorageMap;
@@ -917,6 +920,14 @@ namespace {
cSetRecentEmojisPreload(v);
} break;
case dbiDialogLastPath: {
QString path;
stream >> path;
if (!_checkStreamStatus(stream)) return false;
cSetDialogLastPath(path);
} break;
default:
LOG(("App Error: unknown blockId in _readSetting: %1").arg(blockId));
return false;
@@ -1020,7 +1031,7 @@ namespace {
bool _readOldUserSettings(bool remove = true) {
bool result = false;
QFile file(cWorkingDir() + cDataFile() + qsl("_config"));
QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString()) + qsl("_config"));
if (file.open(QIODevice::ReadOnly)) {
LOG(("App Info: reading old user config.."));
qint32 version = 0;
@@ -1100,7 +1111,7 @@ namespace {
bool _readOldMtpData(bool remove = true) {
bool result = false;
QFile file(cWorkingDir() + cDataFile());
QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString()));
if (file.open(QIODevice::ReadOnly)) {
LOG(("App Info: reading old keys.."));
qint32 version = 0;
@@ -1127,6 +1138,7 @@ 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) + _stringSize(cDialogLastPath());
EncryptedDescriptor data(size);
data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter);
@@ -1141,6 +1153,7 @@ namespace {
data.stream << quint32(dbiDownloadPath) << (cAskDownloadPath() ? QString() : cDownloadPath());
data.stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage());
data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab());
data.stream << quint32(dbiDialogLastPath) << cDialogLastPath();
RecentEmojiPreload v;
v.reserve(cGetRecentEmojis().size());
@@ -1222,7 +1235,7 @@ namespace {
Local::ReadMapState _readMap(const QByteArray &pass) {
uint64 ms = getms();
QByteArray dataNameUtf8 = cDataFile().toUtf8();
QByteArray dataNameUtf8 = (cDataFile() + (cTestMode() ? qsl(":/test/") : QString())).toUtf8();
FileKey dataNameHash[2];
hashMd5(dataNameUtf8.constData(), dataNameUtf8.size(), dataNameHash);
_dataNameKey = dataNameHash[0];
@@ -1271,7 +1284,7 @@ namespace {
DraftsNotReadMap draftsNotReadMap;
StorageMap imagesMap, stickersMap, audiosMap;
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0;
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
while (!map.stream.atEnd()) {
quint32 keyType;
map.stream >> keyType;
@@ -1345,6 +1358,9 @@ namespace {
case lskUserSettings: {
map.stream >> userSettingsKey;
} break;
case lskRecentHashtags: {
map.stream >> recentHashtagsKey;
} break;
default:
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
return Local::ReadMapFailed;
@@ -1369,6 +1385,7 @@ namespace {
_recentStickersKey = recentStickersKey;
_backgroundKey = backgroundKey;
_userSettingsKey = userSettingsKey;
_recentHashtagsKey = recentHashtagsKey;
_oldMapVersion = mapData.version;
if (_oldMapVersion < AppVersion) {
_mapChanged = true;
@@ -1428,9 +1445,10 @@ namespace {
if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.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 (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentStickersKey) 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);
EncryptedDescriptor mapData(mapSize);
if (!_draftsMap.isEmpty()) {
mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
@@ -1465,15 +1483,18 @@ namespace {
if (_locationsKey) {
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
}
if (_userSettingsKey) {
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
}
if (_recentStickersKey) {
mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
}
if (_backgroundKey) {
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey);
}
if (_userSettingsKey) {
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
}
if (_recentHashtagsKey) {
mapData.stream << quint32(lskRecentHashtags) << quint64(_recentHashtagsKey);
}
map.writeEncrypted(mapData);
_mapChanged = false;
@@ -1558,7 +1579,7 @@ namespace Local {
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
FileReadDescriptor settingsData;
if (!readFile(settingsData, qsl("settings"), SafePath)) {
if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath)) {
_readOldSettings();
_readOldUserSettings(false); // needed further in _readUserSettings
_readOldMtpData(false); // needed further in _readMtpData
@@ -1617,7 +1638,7 @@ namespace Local {
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
FileWriteDescriptor settings(qsl("settings"), SafePath);
FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath);
if (_settingsSalt.isEmpty() || !_settingsKey.created()) {
_settingsSalt.resize(LocalEncryptSaltSize);
memset_rand(_settingsSalt.data(), _settingsSalt.size());
@@ -1696,7 +1717,7 @@ namespace Local {
_draftsNotReadMap.clear();
_stickersMap.clear();
_audiosMap.clear();
_locationsKey = _userSettingsKey = _recentStickersKey = _backgroundKey = 0;
_locationsKey = _recentStickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
_mapChanged = true;
_writeMap(WriteMapNow);
@@ -1735,10 +1756,10 @@ namespace Local {
return _oldMapVersion;
}
void writeDraft(const PeerId &peer, const QString &text) {
void writeDraft(const PeerId &peer, const MessageDraft &draft) {
if (!_working()) return;
if (text.isEmpty()) {
if (draft.replyTo <= 0 && draft.text.isEmpty()) {
DraftsMap::iterator i = _draftsMap.find(peer);
if (i != _draftsMap.cend()) {
clearKey(i.value());
@@ -1755,8 +1776,8 @@ namespace Local {
_mapChanged = true;
_writeMap(WriteMapFast);
}
EncryptedDescriptor data(sizeof(quint64) + _stringSize(text));
data.stream << quint64(peer) << text;
EncryptedDescriptor data(sizeof(quint64) + _stringSize(draft.text) + sizeof(qint32));
data.stream << quint64(peer) << draft.text << qint32(draft.replyTo) << qint32(draft.previewCancelled ? 1 : 0);
FileWriteDescriptor file(i.value());
file.writeEncrypted(data);
@@ -1764,24 +1785,27 @@ namespace Local {
}
}
QString readDraft(const PeerId &peer) {
if (!_draftsNotReadMap.remove(peer)) return QString();
MessageDraft readDraft(const PeerId &peer) {
if (!_draftsNotReadMap.remove(peer)) return MessageDraft();
DraftsMap::iterator j = _draftsMap.find(peer);
if (j == _draftsMap.cend()) {
return QString();
return MessageDraft();
}
FileReadDescriptor draft;
if (!readEncryptedFile(draft, j.value())) {
clearKey(j.value());
_draftsMap.erase(j);
return QString();
return MessageDraft();
}
quint64 draftPeer;
QString draftText;
qint32 draftReplyTo = 0, draftPreviewCancelled = 0;
draft.stream >> draftPeer >> draftText;
return (draftPeer == peer) ? draftText : QString();
if (draft.version >= 7021) draft.stream >> draftReplyTo;
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) {
@@ -2092,15 +2116,17 @@ namespace Local {
quint32 size = 0;
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
DocumentData *doc = i->first;
if (doc->status == FileFailed) continue;
// id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type
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);
// 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);
}
EncryptedDescriptor data(size);
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
DocumentData *doc = i->first;
if (doc->status == FileFailed) continue;
data.stream << quint64(doc->id) << qint16(i->second) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type);
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;
}
FileWriteDescriptor file(_recentStickersKey);
file.writeEncrypted(data);
@@ -2122,10 +2148,13 @@ namespace Local {
RecentStickerPack recent;
while (!stickers.stream.atEnd()) {
quint64 id, access;
QString name, mime;
QString name, mime, alt;
qint32 date, dc, size, width, height, type;
qint16 value;
stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type;
if (stickers.version >= 7021) {
stickers.stream >> alt;
}
if (read.contains(id)) continue;
read.insert(id, true);
@@ -2134,7 +2163,7 @@ namespace Local {
if (type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (type == StickerDocument) {
attributes.push_back(MTP_documentAttributeSticker());
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt)));
}
if (width > 0 && height > 0) {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
@@ -2192,7 +2221,90 @@ namespace Local {
}
return false;
}
void writeRecentHashtags() {
if (!_working()) return;
const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags());
if (write.isEmpty() && search.isEmpty()) readRecentHashtags();
if (write.isEmpty() && search.isEmpty()) {
if (_recentHashtagsKey) {
clearKey(_recentHashtagsKey);
_recentHashtagsKey = 0;
_mapChanged = true;
}
_writeMap();
} else {
if (!_recentHashtagsKey) {
_recentHashtagsKey = genKey();
_mapChanged = true;
_writeMap(WriteMapFast);
}
quint32 size = sizeof(quint32) * 2, writeCnt = 0, searchCnt = 0;
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
if (!i->first.isEmpty()) {
size += _stringSize(i->first) + sizeof(quint16);
++writeCnt;
}
}
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
if (!i->first.isEmpty()) {
size += _stringSize(i->first) + sizeof(quint16);
++searchCnt;
}
}
EncryptedDescriptor data(size);
data.stream << quint32(writeCnt) << quint32(searchCnt);
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
}
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
}
FileWriteDescriptor file(_recentHashtagsKey);
file.writeEncrypted(data);
}
}
void readRecentHashtags() {
if (_recentHashtagsWereRead) return;
_recentHashtagsWereRead = true;
if (!_recentHashtagsKey) return;
FileReadDescriptor hashtags;
if (!readEncryptedFile(hashtags, _recentHashtagsKey)) {
clearKey(_recentHashtagsKey);
_recentHashtagsKey = 0;
_writeMap();
return;
}
quint32 writeCount = 0, searchCount = 0;
hashtags.stream >> writeCount >> searchCount;
QString tag;
quint16 count;
RecentHashtagPack write, search;
if (writeCount) {
write.reserve(writeCount);
for (uint32 i = 0; i < writeCount; ++i) {
hashtags.stream >> tag >> count;
write.push_back(qMakePair(tag.trimmed(), count));
}
}
if (searchCount) {
search.reserve(searchCount);
for (uint32 i = 0; i < searchCount; ++i) {
hashtags.stream >> tag >> count;
search.push_back(qMakePair(tag.trimmed(), count));
}
}
cSetRecentWriteHashtags(write);
cSetRecentSearchHashtags(search);
}
struct ClearManagerData {
QThread *thread;
StorageMap images, stickers, audios;
@@ -2244,6 +2356,10 @@ namespace Local {
_recentStickersKey = 0;
_mapChanged = true;
}
if (_recentHashtagsKey) {
_recentHashtagsKey = 0;
_mapChanged = true;
}
_writeMap();
} else {
if (task & ClearManagerStorage) {

View File

@@ -100,8 +100,15 @@ namespace Local {
ReadMapState readMap(const QByteArray &pass);
int32 oldMapVersion();
void writeDraft(const PeerId &peer, const QString &text);
QString readDraft(const PeerId &peer);
struct MessageDraft {
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);
void writeDraftPositions(const PeerId &peer, const MessageCursor &cur);
MessageCursor readDraftPositions(const PeerId &peer);
bool hasDraftPositions(const PeerId &peer);
@@ -131,4 +138,7 @@ namespace Local {
void writeBackground(int32 id, const QImage &img);
bool readBackground();
void writeRecentHashtags();
void readRecentHashtags();
};

View File

@@ -40,7 +40,7 @@ namespace {
QDateTime tm(QDateTime::currentDateTime());
QThread *thread = QThread::currentThread();
MTPThread *mtpThread = dynamic_cast<MTPThread*>(thread);
MTPThread *mtpThread = qobject_cast<MTPThread*>(thread);
uint32 threadId = mtpThread ? mtpThread->getThreadId() : 0;
return QString("[%1 %2-%3]").arg(tm.toString("hh:mm:ss.zzz")).arg(QString("%1").arg(threadId, 2, 10, zero)).arg(++logEntry, 7, 10, zero);

File diff suppressed because it is too large Load Diff

View File

@@ -17,13 +17,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <QtWidgets/QWidget>
#include "gui/flatbutton.h"
#include "dialogswidget.h"
#include "historywidget.h"
#include "profilewidget.h"
#include "overviewwidget.h"
#include "apiwrap.h"
class Window;
struct DialogRow;
@@ -112,11 +110,12 @@ public:
class StackItemHistory : public StackItem {
public:
StackItemHistory(PeerData *peer, int32 lastWidth, int32 lastScrollTop) : StackItem(peer), lastWidth(lastWidth), lastScrollTop(lastScrollTop) {
StackItemHistory(PeerData *peer, int32 lastWidth, int32 lastScrollTop, QList<MsgId> replyReturns) : StackItem(peer), replyReturns(replyReturns), lastWidth(lastWidth), lastScrollTop(lastScrollTop) {
}
StackItemType type() const {
return HistoryStackItem;
}
QList<MsgId> replyReturns;
int32 lastWidth, lastScrollTop;
};
@@ -187,12 +186,12 @@ public:
void start(const MTPUser &user);
void openLocalUrl(const QString &str);
void openUserByName(const QString &name);
void openUserByName(const QString &name, bool toProfile = false);
void startFull(const MTPVector<MTPUser> &users);
bool started();
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
void gotNotifySetting(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings);
bool failNotifySetting(MTPInputNotifyPeer peer);
bool failNotifySetting(MTPInputNotifyPeer peer, const RPCError &error);
void updateNotifySetting(PeerData *peer, bool enabled);
@@ -207,14 +206,11 @@ public:
void windowShown();
void sentDataReceived(uint64 randomId, const MTPmessages_SentMessage &data);
void sentFullDataReceived(uint64 randomId, const MTPmessages_StatedMessage &result); // randomId = 0 - new message, <> 0 - already added new message
void sentFullDatasReceived(const MTPmessages_StatedMessages &result);
void forwardDone(PeerId peer, const MTPmessages_StatedMessages &result);
void sentUpdatesReceived(const MTPUpdates &updates);
void msgUpdated(PeerId peer, const HistoryItem *msg);
void historyToDown(History *hist);
void dialogsToUp();
void newUnreadMsg(History *history, MsgId msgId);
void updUpdated(int32 pts, int32 seq);
void newUnreadMsg(History *history, HistoryItem *item);
void historyWasRead();
void peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg);
@@ -230,9 +226,9 @@ public:
void showBackFromStack();
QRect historyRect() const;
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname);
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo);
void confirmSendImage(const ReadyLocalMedia &img);
void confirmSendImageUncompressed(bool ctrlShiftEnter);
void confirmSendImageUncompressed(bool ctrlShiftEnter, MsgId replyTo);
void cancelSendImage();
void destroyData();
@@ -252,28 +248,27 @@ public:
void shareContactLayer(UserData *contact);
void hiderLayer(HistoryHider *h);
void noHider(HistoryHider *destroyed);
mtpRequestId onForward(const PeerId &peer, bool forwardSelected);
void onForward(const PeerId &peer, bool forwardSelected);
void onShareContact(const PeerId &peer, UserData *contact);
void onSendPaths(const PeerId &peer);
bool selectingPeer();
bool selectingPeer(bool withConfirm = false);
void offerPeer(PeerId peer);
void focusPeerSelect();
void dialogsActivate();
bool leaveChatFailed(PeerData *peer, const RPCError &e);
void deleteHistory(PeerData *peer, const MTPmessages_StatedMessage &result);
void deleteHistory(PeerData *peer, const MTPUpdates &updates);
void deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result);
void deleteMessages(const QVector<MTPint> &ids);
void deletedContact(UserData *user, const MTPcontacts_Link &result);
void deleteHistoryAndContact(UserData *user, const MTPcontacts_Link &result);
void clearHistory(PeerData *peer);
void removeContact(UserData *user);
void addParticipants(ChatData *chat, const QVector<UserData*> &users);
void addParticipantDone(ChatData *chat, const MTPmessages_StatedMessage &result);
bool addParticipantFail(ChatData *chat, const RPCError &e);
void kickParticipant(ChatData *chat, UserData *user);
void kickParticipantDone(ChatData *chat, const MTPmessages_StatedMessage &result);
bool kickParticipantFail(ChatData *chat, const RPCError &e);
void checkPeerHistory(PeerData *peer);
@@ -287,8 +282,9 @@ public:
DialogsIndexed &contactsList();
void sendMessage(History *history, const QString &text);
void sendPreparedText(History *hist, const QString &text);
void sendMessage(History *history, 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);
@@ -301,7 +297,7 @@ public:
void changingMsgId(HistoryItem *row, MsgId newId);
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void itemResized(HistoryItem *row);
void itemResized(HistoryItem *row, bool scrollToIt = false);
void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
void peerUsernameChanged(PeerData *peer);
@@ -325,7 +321,20 @@ public:
bool chatBackgroundLoading();
void checkChatBackground();
ImagePtr newBackgroundThumb();
ApiWrap *api();
void updateReplyTo();
void pushReplyReturn(HistoryItem *item);
bool hasForwardingItems();
void fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview);
void updateForwardingTexts();
void cancelForwarding();
void finishForwarding(History *hist); // send them
void webPageUpdated(WebPageData *page);
~MainWidget();
signals:
@@ -340,6 +349,8 @@ signals:
public slots:
void webPagesUpdate();
void videoLoadProgress(mtpFileLoader *loader);
void videoLoadFailed(mtpFileLoader *loader, bool started);
void videoLoadRetry();
@@ -383,6 +394,7 @@ public slots:
private:
void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result);
void msgsWereDeleted(const MTPmessages_AffectedMessages &result);
void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req);
bool _started;
@@ -393,6 +405,13 @@ private:
QList<uint64> _resendImgRandomIds;
SelectedItemSet _toForward;
Text _toForwardFrom, _toForwardText;
int32 _toForwardNameVersion;
QMap<WebPageId, bool> _webPagesUpdated;
QTimer _webPageUpdater;
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);
@@ -407,7 +426,7 @@ private:
void handleUpdates(const MTPUpdates &updates);
bool updateFail(const RPCError &e);
void usernameResolveDone(const MTPUser &user);
void usernameResolveDone(bool toProfile, const MTPUser &user);
bool usernameResolveFail(QString name, const RPCError &error);
void hideAll();
@@ -435,8 +454,10 @@ private:
Dropdown _mediaType;
int32 _mediaTypeMask;
int updPts, updDate, updQts, updSeq;
int updGoodPts, updLastPts, updPtsCount;
int updDate, updQts, updSeq;
bool updInited;
int updSkipPtsUpdateLevel;
SingleTimer noUpdatesTimer;
mtpRequestId _onlineRequest;
@@ -454,11 +475,24 @@ private:
typedef QMap<PeerData*, mtpRequestId> OverviewsPreload;
OverviewsPreload _overviewPreload[OverviewCount], _overviewLoad[OverviewCount];
enum PtsSkippedQueue {
SkippedUpdate,
SkippedUpdates,
SkippedSentMessage,
SkippedStatedMessage,
SkippedStatedMessages
};
uint64 ptsKey(PtsSkippedQueue queue);
void applySkippedPtsUpdates();
void clearSkippedPtsUpdates();
bool updPtsUpdated(int pts, int ptsCount);
QMap<uint64, PtsSkippedQueue> _byPtsQueue;
QMap<uint64, MTPUpdate> _byPtsUpdate;
QMap<uint64, MTPUpdates> _byPtsUpdates;
QMap<uint64, MTPmessages_SentMessage> _byPtsSentMessage;
SingleTimer _byPtsTimer;
QMap<int32, MTPUpdates> _bySeqUpdates;
QMap<int32, MTPmessages_SentMessage> _bySeqSentMessage;
QMap<int32, MTPmessages_StatedMessage> _bySeqStatedMessage;
QMap<int32, MTPmessages_StatedMessages> _bySeqStatedMessages;
QMap<int32, int32> _bySeqPart;
SingleTimer _bySeqTimer;
int32 _failDifferenceTimeout; // growing timeout for getDifference calls, if it fails
@@ -473,4 +507,6 @@ private:
App::WallPaper *_background;
ApiWrap *_api;
};

View File

@@ -517,7 +517,19 @@ void MediaView::showPhoto(PhotoData *photo) {
_x = (_avail.width() - _w) / 2;
_y = st::medviewPolaroid.top() + (_avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar - _h) / 2;
_width = _w;
_from = App::user(_photo->user);
if (_photo->user == WebPageUserId && _msgid) {
if (HistoryItem *item = App::histItemById(_msgid)) {
if (dynamic_cast<HistoryForwarded*>(item)) {
_from = static_cast<HistoryForwarded*>(item)->fromForwarded();
} else {
_from = item->from();
}
} else {
_from = App::user(_photo->user);
}
} else {
_from = App::user(_photo->user);
}
updateControls();
_photo->full->load();
if (isHidden()) {

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