Merge branch 'master' into dev

This commit is contained in:
John Preston 2016-05-16 18:48:32 +03:00
commit 41b6f830aa
68 changed files with 4663 additions and 2353 deletions

View File

@ -599,7 +599,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}"; "lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "In reply to"; "lng_in_reply_to" = "In reply to";
"lng_edited" = "Edited"; "lng_edited" = "edited";
"lng_edited_date" = "Edited: {date}"; "lng_edited_date" = "Edited: {date}";
"lng_cancel_edit_post_sure" = "Cancel editing?"; "lng_cancel_edit_post_sure" = "Cancel editing?";
"lng_cancel_edit_post_yes" = "Yes"; "lng_cancel_edit_post_yes" = "Yes";
@ -897,7 +897,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}"; "lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
"lng_new_version_minor" = "— Bug fixes and other minor improvements"; "lng_new_version_minor" = "— Bug fixes and other minor improvements";
"lng_new_version_text" = "BOTS 2.0\n\n— Introducing Bot API 2.0, the biggest update to our bot platform since June 2015.\n— Bots can now update existing messages on the fly as you interact with them.\n— New Inline keyboards with callback, 'open URL' or 'switch to inline mode' buttons help create seamless interfaces.\n— Inline bots can now send all attachments supported by Telegram (videos, music, stickers, locations, etc.).\n— Try out these sample bots to see what's coming your way soon: @music, @sticker, @youtube, @foursquare\n\nMore info: {link}"; "lng_new_version_text" = "— Edit your messages everywhere within 2 days after posting (press the up arrow button to edit your last message).\n— Mention people in groups by typing @ and selecting them from the list — even if they don't have a username.\n\nMore: {link}";
"lng_menu_insert_unicode" = "Insert Unicode control character"; "lng_menu_insert_unicode" = "Insert Unicode control character";

View File

@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "Lade Linkvorschau..."; "lng_preview_loading" = "Lade Linkvorschau...";
"lng_profile_chat_unaccessible" = "Gruppe nicht verfügbar"; "lng_profile_chat_unaccessible" = "Gruppe nicht verfügbar";
"lng_topbar_info" = "Info";
"lng_profile_about_section" = "Info"; "lng_profile_about_section" = "Info";
"lng_profile_description_section" = "Beschreibung"; "lng_profile_description_section" = "Beschreibung";
"lng_profile_settings_section" = "Einstellungen"; "lng_profile_settings_section" = "Einstellungen";
@ -600,6 +599,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_channel_via" = "Weitergeleitet aus {channel} via {inline_bot}"; "lng_forwarded_channel_via" = "Weitergeleitet aus {channel} via {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Antwort auf"; "lng_in_reply_to" = "Antwort auf";
"lng_edited" = "bearbeitet";
"lng_edited_date" = "Bearbeitet: {date}";
"lng_cancel_edit_post_sure" = "Bearbeitung abbrechen?";
"lng_cancel_edit_post_yes" = "Ja";
"lng_cancel_edit_post_no" = "Nein";
"lng_bot_share_location_unavailable" = "Teilen von Standorten ist derzeit bei Telegram Desktop nicht möglich."; "lng_bot_share_location_unavailable" = "Teilen von Standorten ist derzeit bei Telegram Desktop nicht möglich.";
"lng_bot_inline_geo_unavailable" = "Dieser Bot braucht deinen aktuellen Standort. Die Funktion ist bei Telegram Desktop derzeit nicht verfügbar."; "lng_bot_inline_geo_unavailable" = "Dieser Bot braucht deinen aktuellen Standort. Die Funktion ist bei Telegram Desktop derzeit nicht verfügbar.";
@ -893,12 +897,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop wurde aktualisiert auf Version {version}\n\n{changes}\n\nGesamter Versionsverlauf:\n{link}"; "lng_new_version_wrap" = "Telegram Desktop wurde aktualisiert auf Version {version}\n\n{changes}\n\nGesamter Versionsverlauf:\n{link}";
"lng_new_version_minor" = "— Fehlerbehebungen und Softwareoptimierungen"; "lng_new_version_minor" = "— Fehlerbehebungen und Softwareoptimierungen";
"lng_new_version_text" = "— Optische Verbesserungen (u.a. runde Profilbilder)"; "lng_new_version_text" = "— Bearbeite deine Nachrichten nachträglich (innerhalb von 2 Tagen).\n— Erwähne Leute in Gruppen - ohne das diese einen Benutzernamen haben müssen (einfach @ eingeben und aus der Liste auswählen).\n\nMehr: {link}";
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen"; "lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";
"lng_full_name" = "{first_name} {last_name}"; "lng_full_name" = "{first_name} {last_name}";
// Not used
"lng_topbar_info" = "Info";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Öffnen mit..."; "lng_wnd_choose_program_menu" = "Öffnen mit...";

View File

@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "Obteniendo enlace..."; "lng_preview_loading" = "Obteniendo enlace...";
"lng_profile_chat_unaccessible" = "El grupo es inaccesible"; "lng_profile_chat_unaccessible" = "El grupo es inaccesible";
"lng_topbar_info" = "Información";
"lng_profile_about_section" = "Acerca de"; "lng_profile_about_section" = "Acerca de";
"lng_profile_description_section" = "Descripción"; "lng_profile_description_section" = "Descripción";
"lng_profile_settings_section" = "Ajustes"; "lng_profile_settings_section" = "Ajustes";
@ -600,6 +599,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_channel_via" = "Reenviado desde {channel} vía {inline_bot}"; "lng_forwarded_channel_via" = "Reenviado desde {channel} vía {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Respondiendo a"; "lng_in_reply_to" = "Respondiendo a";
"lng_edited" = "editado";
"lng_edited_date" = "Editado: {date}";
"lng_cancel_edit_post_sure" = "¿Cancelar edición?";
"lng_cancel_edit_post_yes" = "Sí";
"lng_cancel_edit_post_no" = "No";
"lng_bot_share_location_unavailable" = "Lo sentimos, compartir la ubicación no está disponible actualmente en Telegram Desktop."; "lng_bot_share_location_unavailable" = "Lo sentimos, compartir la ubicación no está disponible actualmente en Telegram Desktop.";
"lng_bot_inline_geo_unavailable" = "Lo sentimos, este bot requiere compartir la ubicación.\nNo está disponible en Telegram Desktop."; "lng_bot_inline_geo_unavailable" = "Lo sentimos, este bot requiere compartir la ubicación.\nNo está disponible en Telegram Desktop.";
@ -893,12 +897,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop ha sido actualizada a la versión {version}\n\n{changes}\n\nEl historial completo está disponible aquí:\n{link}"; "lng_new_version_wrap" = "Telegram Desktop ha sido actualizada a la versión {version}\n\n{changes}\n\nEl historial completo está disponible aquí:\n{link}";
"lng_new_version_minor" = "— Corrección de errores y otras mejoras menores"; "lng_new_version_minor" = "— Corrección de errores y otras mejoras menores";
"lng_new_version_text" = "— Mejoras visuales"; "lng_new_version_text" = "— Edita tus mensajes, en todas partes, hasta 2 días después del envío.\n— Menciona a personas en grupos escribiendo @ y eligiéndolas desde la lista, incluso si no tienen un alias.\n\nMás: {link}";
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode"; "lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";
"lng_full_name" = "{first_name} {last_name}"; "lng_full_name" = "{first_name} {last_name}";
// Not used
"lng_topbar_info" = "Información";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Elegir programa predeterminado..."; "lng_wnd_choose_program_menu" = "Elegir programa predeterminado...";

View File

@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "Recupero le info del link..."; "lng_preview_loading" = "Recupero le info del link...";
"lng_profile_chat_unaccessible" = "Gruppo non accessibile"; "lng_profile_chat_unaccessible" = "Gruppo non accessibile";
"lng_topbar_info" = "Info";
"lng_profile_about_section" = "Info"; "lng_profile_about_section" = "Info";
"lng_profile_description_section" = "Descrizione"; "lng_profile_description_section" = "Descrizione";
"lng_profile_settings_section" = "Impostazioni"; "lng_profile_settings_section" = "Impostazioni";
@ -600,6 +599,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_channel_via" = "Inoltrato da {channel} via {inline_bot}"; "lng_forwarded_channel_via" = "Inoltrato da {channel} via {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "In risposta a"; "lng_in_reply_to" = "In risposta a";
"lng_edited" = "modificato";
"lng_edited_date" = "Modificato: {date}";
"lng_cancel_edit_post_sure" = "Annullare la modifica?";
"lng_cancel_edit_post_yes" = "Sì";
"lng_cancel_edit_post_no" = "No";
"lng_bot_share_location_unavailable" = "Spiacenti, la condivisione della posizione non è al momento disponibile su Telegram Desktop."; "lng_bot_share_location_unavailable" = "Spiacenti, la condivisione della posizione non è al momento disponibile su Telegram Desktop.";
"lng_bot_inline_geo_unavailable" = "Spiacenti, questo bot richiede la condivisione della posizione. Non è disponibile su Telegram Desktop."; "lng_bot_inline_geo_unavailable" = "Spiacenti, questo bot richiede la condivisione della posizione. Non è disponibile su Telegram Desktop.";
@ -628,7 +632,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_media_auto_photo" = "Download automatico foto"; "lng_media_auto_photo" = "Download automatico foto";
"lng_media_auto_audio" = "Download automatico messaggi vocali"; "lng_media_auto_audio" = "Download automatico messaggi vocali";
"lng_media_auto_gif" = "Download automatico GIF"; "lng_media_auto_gif" = "Download automatico GIF";
"lng_media_auto_private_chats" = "Chat"; "lng_media_auto_private_chats" = "Chat private";
"lng_media_auto_groups" = "Gruppi e canali"; "lng_media_auto_groups" = "Gruppi e canali";
"lng_media_auto_play" = "Autoriproduzione"; "lng_media_auto_play" = "Autoriproduzione";
@ -893,12 +897,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli aggiornamenti è disponibile qui:\n{link}"; "lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli aggiornamenti è disponibile qui:\n{link}";
"lng_new_version_minor" = "— Risoluzione di problemi e altri miglioramenti minori"; "lng_new_version_minor" = "— Risoluzione di problemi e altri miglioramenti minori";
"lng_new_version_text" = "— Miglioramenti visivi"; "lng_new_version_text" = "— Modifica i tuoi messaggi ovunque entro 2 giorni dall'invio.\n— Menziona le persone nei gruppi digitando @ e selezionandole dalla lista — anche se non hanno un username.\n\nPiù informazioni: {link}";
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode"; "lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";
"lng_full_name" = "{first_name} {last_name}"; "lng_full_name" = "{first_name} {last_name}";
// Not used
"lng_topbar_info" = "Info";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Scegli programma predefinito..."; "lng_wnd_choose_program_menu" = "Scegli programma predefinito...";

View File

@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "링크 정보를 가져오는 중.."; "lng_preview_loading" = "링크 정보를 가져오는 중..";
"lng_profile_chat_unaccessible" = "그룹에 접근할 수 없습니다."; "lng_profile_chat_unaccessible" = "그룹에 접근할 수 없습니다.";
"lng_topbar_info" = "정보";
"lng_profile_about_section" = "정보"; "lng_profile_about_section" = "정보";
"lng_profile_description_section" = "설명"; "lng_profile_description_section" = "설명";
"lng_profile_settings_section" = "환경설정"; "lng_profile_settings_section" = "환경설정";
@ -594,12 +593,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "클립보드에 링크가 복사되었습니다."; "lng_channel_public_link_copied" = "클립보드에 링크가 복사되었습니다.";
"lng_forwarded" = "{user}로 부터 전달 받음"; "lng_forwarded" = "{user}님으로 부터 전달 받음";
"lng_forwarded_channel" = "{channel}로 부터 전달 받음"; "lng_forwarded_channel" = "{channel}로 부터 전달 받음";
"lng_forwarded_via" = "{inline_bot}을 {user}로 부터 전달 받음"; "lng_forwarded_via" = "{inline_bot}을 {user}로 부터 전달 받음";
"lng_forwarded_channel_via" = "{inline_bot}을 {channel}로 부터 전달 받음"; "lng_forwarded_channel_via" = "{inline_bot}을 {channel}로 부터 전달 받음";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "다음 유저에게 답장 :"; "lng_in_reply_to" = "다음 유저에게 답장 :";
"lng_edited" = "수정됨";
"lng_edited_date" = "수정됨: {date}";
"lng_cancel_edit_post_sure" = "수정을 취소하겠습니까?";
"lng_cancel_edit_post_yes" = "네";
"lng_cancel_edit_post_no" = "아니요";
"lng_bot_share_location_unavailable" = "죄송합니다, 위치 공유는 텔레그램 테스크탑에서는 현재 지원을 하고 있지 않습니다."; "lng_bot_share_location_unavailable" = "죄송합니다, 위치 공유는 텔레그램 테스크탑에서는 현재 지원을 하고 있지 않습니다.";
"lng_bot_inline_geo_unavailable" = "죄송합니다, 위치 공유는 텔레그램 테스크탑에서는 현재 지원을 하고 있지 않습니다."; "lng_bot_inline_geo_unavailable" = "죄송합니다, 위치 공유는 텔레그램 테스크탑에서는 현재 지원을 하고 있지 않습니다.";
@ -893,12 +897,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}"; "lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}";
"lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상"; "lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상";
"lng_new_version_text" = "— 비주얼 향상"; "lng_new_version_text" = "— 메시지 작성 후 2일내 수정 기능\n— 그룹내에서 @를 입력하고 상대방을 선택하여 멘션 기능 ㅡ 아이디가 없어도 가능\n\n자세한 설명: {link}";
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요."; "lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";
"lng_full_name" = "{last_name} {first_name}"; "lng_full_name" = "{last_name} {first_name}";
// Not used
"lng_topbar_info" = "정보";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "기본 실행 프로그램을 선택해주세요.."; "lng_wnd_choose_program_menu" = "기본 실행 프로그램을 선택해주세요..";

View File

@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "Link-preview ophalen..."; "lng_preview_loading" = "Link-preview ophalen...";
"lng_profile_chat_unaccessible" = "Groep is ontoegankelijk"; "lng_profile_chat_unaccessible" = "Groep is ontoegankelijk";
"lng_topbar_info" = "Info";
"lng_profile_about_section" = "Over"; "lng_profile_about_section" = "Over";
"lng_profile_description_section" = "Beschrijving"; "lng_profile_description_section" = "Beschrijving";
"lng_profile_settings_section" = "Instellingen"; "lng_profile_settings_section" = "Instellingen";
@ -600,6 +599,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_channel_via" = "Doorgestuurd van {channel} via {inline_bot}"; "lng_forwarded_channel_via" = "Doorgestuurd van {channel} via {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Antwoord op"; "lng_in_reply_to" = "Antwoord op";
"lng_edited" = "bewerkt";
"lng_edited_date" = "Bewerkt: {date}";
"lng_cancel_edit_post_sure" = "Bewerken annuleren?";
"lng_cancel_edit_post_yes" = "Ja";
"lng_cancel_edit_post_no" = "Nee";
"lng_bot_share_location_unavailable" = "Sorry, locatie delen is nog niet beschikbaar via Telegram Desktop."; "lng_bot_share_location_unavailable" = "Sorry, locatie delen is nog niet beschikbaar via Telegram Desktop.";
"lng_bot_inline_geo_unavailable" = "Deze bot heeft je locatie nodig, dit is\nnog niet beschikbaar via Telegram Desktop."; "lng_bot_inline_geo_unavailable" = "Deze bot heeft je locatie nodig, dit is\nnog niet beschikbaar via Telegram Desktop.";
@ -893,12 +897,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram is bijgewerkt naar versie {version}\n\n{changes} \n\nVolledige versiegeschiedenis is hier te vinden:\n{link}"; "lng_new_version_wrap" = "Telegram is bijgewerkt naar versie {version}\n\n{changes} \n\nVolledige versiegeschiedenis is hier te vinden:\n{link}";
"lng_new_version_minor" = "— Probleemoplossing en andere kleine verbeteringen"; "lng_new_version_minor" = "— Probleemoplossing en andere kleine verbeteringen";
"lng_new_version_text" = "— Visuele verbeteringen"; "lng_new_version_text" = "— 2 dagen om je berichten naderhand te bewerken.\n— Anderen vermelden in groepen via '@', kies daarna de persoon uit de lijst. Ook als ze geen gebruikersnaam hebben!\n\nMeer over deze update: {link}";
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen"; "lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";
"lng_full_name" = "{first_name} {last_name}"; "lng_full_name" = "{first_name} {last_name}";
// Not used
"lng_topbar_info" = "Info";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Standaardprogramma selecteren... "; "lng_wnd_choose_program_menu" = "Standaardprogramma selecteren... ";

View File

@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "Obtendo Informações do Link..."; "lng_preview_loading" = "Obtendo Informações do Link...";
"lng_profile_chat_unaccessible" = "Grupo inacessível"; "lng_profile_chat_unaccessible" = "Grupo inacessível";
"lng_topbar_info" = "Info";
"lng_profile_about_section" = "Sobre"; "lng_profile_about_section" = "Sobre";
"lng_profile_description_section" = "Descrição"; "lng_profile_description_section" = "Descrição";
"lng_profile_settings_section" = "Configurações"; "lng_profile_settings_section" = "Configurações";
@ -600,6 +599,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_channel_via" = "Encaminhado de {channel} via {inline_bot}"; "lng_forwarded_channel_via" = "Encaminhado de {channel} via {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Em resposta a"; "lng_in_reply_to" = "Em resposta a";
"lng_edited" = "editado";
"lng_edited_date" = "Editado: {date}";
"lng_cancel_edit_post_sure" = "Cancelar edição?";
"lng_cancel_edit_post_yes" = "Sim";
"lng_cancel_edit_post_no" = "Não";
"lng_bot_share_location_unavailable" = "O compartilhamento de localização está atualmente indisponível no Telegram Desktop."; "lng_bot_share_location_unavailable" = "O compartilhamento de localização está atualmente indisponível no Telegram Desktop.";
"lng_bot_inline_geo_unavailable" = "Esse bot requer compartilhamento de localização\nIsso não está disponível no Telegram Desktop."; "lng_bot_inline_geo_unavailable" = "Esse bot requer compartilhamento de localização\nIsso não está disponível no Telegram Desktop.";
@ -893,12 +897,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop foi atualizado para a versão {version}\n\n{changes}\n\nHistórico completo de mudanças disponível aqui:\n{link}"; "lng_new_version_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 melhorias menores"; "lng_new_version_minor" = "— Resolução de bugs e outras melhorias menores";
"lng_new_version_text" = "— Melhorias no visual"; "lng_new_version_text" = "— Edite suas mensagens em qualquer lugar, até 2 dias após postagem.\n— Mencione pessoas nos grupos digitando @ e selecionando-os através da lista — mesmo que eles não tenham nome de usuário.\n\nMais: {link}";
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode"; "lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";
"lng_full_name" = "{first_name} {last_name}"; "lng_full_name" = "{first_name} {last_name}";
// Not used
"lng_topbar_info" = "Info";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Escolher programa padrão..."; "lng_wnd_choose_program_menu" = "Escolher programa padrão...";

View File

@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,48,0 FILEVERSION 0,9,49,0
PRODUCTVERSION 0,9,48,0 PRODUCTVERSION 0,9,49,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0" BLOCK "040904b0"
BEGIN BEGIN
VALUE "CompanyName", "Telegram Messenger LLP" VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.9.48.0" VALUE "FileVersion", "0.9.49.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.48.0" VALUE "ProductVersion", "0.9.49.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,48,0 FILEVERSION 0,9,49,0
PRODUCTVERSION 0,9,48,0 PRODUCTVERSION 0,9,49,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -43,10 +43,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Telegram Messenger LLP" VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Updater" VALUE "FileDescription", "Telegram Updater"
VALUE "FileVersion", "0.9.48.0" VALUE "FileVersion", "0.9.49.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.48.0" VALUE "ProductVersion", "0.9.49.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -37,17 +37,19 @@ ApiWrap::ApiWrap(QObject *parent) : QObject(parent)
void ApiWrap::init() { void ApiWrap::init() {
} }
void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback) { void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback) {
MessageDataRequest::CallbackPtr pcallback(callback);
MessageDataRequest &req(channel ? _channelMessageDataRequests[channel][msgId] : _messageDataRequests[msgId]); MessageDataRequest &req(channel ? _channelMessageDataRequests[channel][msgId] : _messageDataRequests[msgId]);
if (callback) {
MessageDataRequest::CallbackPtr pcallback(callback.release());
req.callbacks.append(pcallback); req.callbacks.append(pcallback);
}
if (!req.req) _messageDataResolveDelayed->call(); if (!req.req) _messageDataResolveDelayed->call();
} }
ApiWrap::MessageIds ApiWrap::collectMessageIds(const MessageDataRequests &requests) { ApiWrap::MessageIds ApiWrap::collectMessageIds(const MessageDataRequests &requests) {
MessageIds result; MessageIds result;
result.reserve(requests.size()); result.reserve(requests.size());
for (MessageDataRequests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) { for (auto i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
if (i.value().req > 0) continue; if (i.value().req > 0) continue;
result.push_back(MTP_int(i.key())); result.push_back(MTP_int(i.key()));
} }
@ -56,7 +58,7 @@ ApiWrap::MessageIds ApiWrap::collectMessageIds(const MessageDataRequests &reques
ApiWrap::MessageDataRequests *ApiWrap::messageDataRequests(ChannelData *channel, bool onlyExisting) { ApiWrap::MessageDataRequests *ApiWrap::messageDataRequests(ChannelData *channel, bool onlyExisting) {
if (channel) { if (channel) {
ChannelMessageDataRequests::iterator i = _channelMessageDataRequests.find(channel); auto i = _channelMessageDataRequests.find(channel);
if (i == _channelMessageDataRequests.cend()) { if (i == _channelMessageDataRequests.cend()) {
if (onlyExisting) return 0; if (onlyExisting) return 0;
i = _channelMessageDataRequests.insert(channel, MessageDataRequests()); i = _channelMessageDataRequests.insert(channel, MessageDataRequests());
@ -72,12 +74,12 @@ void ApiWrap::resolveMessageDatas() {
MessageIds ids = collectMessageIds(_messageDataRequests); MessageIds ids = collectMessageIds(_messageDataRequests);
if (!ids.isEmpty()) { if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotMessageDatas, (ChannelData*)nullptr), RPCFailHandlerPtr(), 0, 5); mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotMessageDatas, (ChannelData*)nullptr), RPCFailHandlerPtr(), 0, 5);
for (MessageDataRequests::iterator i = _messageDataRequests.begin(); i != _messageDataRequests.cend(); ++i) { for (auto &request : _messageDataRequests) {
if (i.value().req > 0) continue; if (request.req > 0) continue;
i.value().req = req; request.req = req;
} }
} }
for (ChannelMessageDataRequests::iterator j = _channelMessageDataRequests.begin(); j != _channelMessageDataRequests.cend();) { for (auto j = _channelMessageDataRequests.begin(); j != _channelMessageDataRequests.cend();) {
if (j->isEmpty()) { if (j->isEmpty()) {
j = _channelMessageDataRequests.erase(j); j = _channelMessageDataRequests.erase(j);
continue; continue;
@ -85,9 +87,9 @@ void ApiWrap::resolveMessageDatas() {
MessageIds ids = collectMessageIds(j.value()); MessageIds ids = collectMessageIds(j.value());
if (!ids.isEmpty()) { if (!ids.isEmpty()) {
mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotMessageDatas, j.key()), RPCFailHandlerPtr(), 0, 5); mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotMessageDatas, j.key()), RPCFailHandlerPtr(), 0, 5);
for (MessageDataRequests::iterator i = j->begin(); i != j->cend(); ++i) { for (auto &request : *j) {
if (i.value().req > 0) continue; if (request.req > 0) continue;
i.value().req = req; request.req = req;
} }
} }
++j; ++j;
@ -128,10 +130,10 @@ void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &
} }
MessageDataRequests *requests(messageDataRequests(channel, true)); MessageDataRequests *requests(messageDataRequests(channel, true));
if (requests) { if (requests) {
for (MessageDataRequests::iterator i = requests->begin(); i != requests->cend();) { for (auto i = requests->begin(); i != requests->cend();) {
if (i.value().req == req) { if (i.value().req == req) {
for (MessageDataRequest::Callbacks::const_iterator j = i.value().callbacks.cbegin(), e = i.value().callbacks.cend(); j != e; ++j) { for_const (auto &callback, i.value().callbacks) {
(*j)->call(channel, i.key()); callback->call(channel, i.key());
} }
i = requests->erase(i); i = requests->erase(i);
} else { } else {

View File

@ -29,7 +29,7 @@ public:
void init(); void init();
typedef SharedCallback<void, ChannelData*, MsgId> RequestMessageDataCallback; typedef SharedCallback<void, ChannelData*, MsgId> RequestMessageDataCallback;
void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback); void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
void requestFullPeer(PeerData *peer); void requestFullPeer(PeerData *peer);
void requestPeer(PeerData *peer); void requestPeer(PeerData *peer);

View File

@ -968,7 +968,9 @@ namespace {
peerId = peerFromUser(m.vfrom_id); peerId = peerFromUser(m.vfrom_id);
} }
if (HistoryItem *existing = App::histItemById(peerToChannel(peerId), m.vid.v)) { if (HistoryItem *existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
existing->setText(qs(m.vmessage), m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText()); auto text = qs(m.vmessage);
auto entities = m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText();
existing->setText({ text, entities });
existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr); existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr);
existing->setViewsCount(m.has_views() ? m.vviews.v : -1); existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
existing->addToOverview(AddToOverviewNew); existing->addToOverview(AddToOverviewNew);

View File

@ -1031,10 +1031,10 @@ void AppClass::checkMapVersion() {
if (Local::oldMapVersion() < AppVersion) { if (Local::oldMapVersion() < AppVersion) {
if (Local::oldMapVersion()) { if (Local::oldMapVersion()) {
QString versionFeatures; QString versionFeatures;
if ((cAlphaVersion() || cBetaVersion()) && Local::oldMapVersion() < 9041) { if ((cAlphaVersion() || cBetaVersion()) && Local::oldMapVersion() < 9049) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Select and copy text in photo / video captions and web page previews\n\xe2\x80\x94 Media player shortcuts are enabled only when player is opened"); // versionFeatures = QString::fromUtf8("\xe2\x80\x94 Select and copy text in photo / video captions and web page previews\n\xe2\x80\x94 Media player shortcuts are enabled only when player is opened");
// versionFeatures = langNewVersionText(); versionFeatures = langNewVersionText();
} else if (Local::oldMapVersion() < 9041) { } else if (Local::oldMapVersion() < 9049) {
versionFeatures = langNewVersionText(); versionFeatures = langNewVersionText();
} else { } else {
versionFeatures = lang(lng_new_version_minor).trimmed(); versionFeatures = lang(lng_new_version_minor).trimmed();

View File

@ -414,7 +414,7 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth)
image = doc->thumb; image = doc->thumb;
} break; } break;
} }
caption = media->getCaption(); caption = media->getCaption().text;
} }
if ((!_animated && (dimensions.isEmpty() || doc)) || image->isNull()) { if ((!_animated && (dimensions.isEmpty() || doc)) || image->isNull()) {
_animated = false; _animated = false;
@ -492,7 +492,8 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth)
_field->setMaxLength(MaxPhotoCaption); _field->setMaxLength(MaxPhotoCaption);
_field->setCtrlEnterSubmit(CtrlEnterSubmitBoth); _field->setCtrlEnterSubmit(CtrlEnterSubmitBoth);
} else { } else {
QString text = textApplyEntities(msg->originalText(), msg->originalEntities()); auto original = msg->originalText();
QString text = textApplyEntities(original.text, original.entities);
_field = new InputArea(this, st::editTextArea, lang(lng_photo_caption), text); _field = new InputArea(this, st::editTextArea, lang(lng_photo_caption), text);
// _field->setMaxLength(MaxMessageSize); // entities can make text in input field larger but still valid // _field->setMaxLength(MaxMessageSize); // entities can make text in input field larger but still valid
_field->setCtrlEnterSubmit(cCtrlEnter() ? CtrlEnterSubmitCtrlEnter : CtrlEnterSubmitEnter); _field->setCtrlEnterSubmit(cCtrlEnter() ? CtrlEnterSubmitCtrlEnter : CtrlEnterSubmitEnter);

View File

@ -166,6 +166,16 @@ template <typename T, size_t N> char(&ArraySizeHelper(T(&array)[N]))[N];
#define qsl(s) QStringLiteral(s) #define qsl(s) QStringLiteral(s)
#define qstr(s) QLatin1String(s, sizeof(s) - 1) #define qstr(s) QLatin1String(s, sizeof(s) - 1)
// For QFlags<> declared in private section of a class we need to declare
// operators from Q_DECLARE_OPERATORS_FOR_FLAGS as friend functions.
#define Q_DECLARE_FRIEND_INCOMPATIBLE_FLAGS(Flags) \
friend Q_DECL_CONSTEXPR QIncompatibleFlag operator|(Flags::enum_type f1, int f2) Q_DECL_NOTHROW;
#define Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(Flags) \
friend Q_DECL_CONSTEXPR QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) Q_DECL_NOTHROW; \
friend Q_DECL_CONSTEXPR QFlags<Flags::enum_type> operator|(Flags::enum_type f1, QFlags<Flags::enum_type> f2) Q_DECL_NOTHROW; \
Q_DECLARE_FRIEND_INCOMPATIBLE_FLAGS(Flags)
// using for_const instead of plain range-based for loop to ensure usage of const_iterator // using for_const instead of plain range-based for loop to ensure usage of const_iterator
// it is important for the copy-on-write Qt containers // it is important for the copy-on-write Qt containers
// if you have "QVector<T*> v" then "for (T * const p : v)" will still call QVector::detach(), // if you have "QVector<T*> v" then "for (T * const p : v)" will still call QVector::detach(),

View File

@ -61,3 +61,17 @@ bool ClickHandler::setActive(const ClickHandlerPtr &p, ClickHandlerHost *host) {
} }
return true; return true;
} }
QString ClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const {
return QString();
}
TextWithEntities ClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
return { QString(), EntitiesInText() };
}
TextWithEntities ClickHandler::simpleTextWithEntity(const EntityInText &entity) const {
TextWithEntities result;
result.entities.push_back(entity);
return result;
}

View File

@ -23,6 +23,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
class ClickHandler; class ClickHandler;
using ClickHandlerPtr = QSharedPointer<ClickHandler>; using ClickHandlerPtr = QSharedPointer<ClickHandler>;
enum ExpandLinksMode {
ExpandLinksNone,
ExpandLinksShortened,
ExpandLinksAll,
};
class ClickHandlerHost { class ClickHandlerHost {
protected: protected:
@ -35,35 +41,44 @@ protected:
}; };
class EntityInText;
struct TextWithEntities;
class ClickHandler { class ClickHandler {
public: public:
virtual void onClick(Qt::MouseButton) const = 0;
virtual QString tooltip() const {
return QString();
}
virtual void copyToClipboard() const {
}
virtual QString copyToClipboardContextItem() const {
return QString();
}
virtual QString text() const {
return QString();
}
virtual QString dragText() const {
return text();
}
virtual ~ClickHandler() { virtual ~ClickHandler() {
} }
// this method should be called on mouse over a click handler virtual void onClick(Qt::MouseButton) const = 0;
// it returns true if something was changed or false otherwise
// What text to show in a tooltip when mouse is over that click handler as a link in Text.
virtual QString tooltip() const {
return QString();
}
// What to drop in the input fields when dragging that click handler as a link from Text.
virtual QString dragText() const {
return QString();
}
// Copy to clipboard support.
virtual void copyToClipboard() const {
}
virtual QString copyToClipboardContextItemText() const {
return QString();
}
// Entities in text support.
// This method returns empty string if just textPart should be used (nothing to expand).
virtual QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const;
virtual TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const;
// This method should be called on mouse over a click handler.
// It returns true if the active handler was changed or false otherwise.
static bool setActive(const ClickHandlerPtr &p, ClickHandlerHost *host = nullptr); static bool setActive(const ClickHandlerPtr &p, ClickHandlerHost *host = nullptr);
// this method should be called when mouse leaves the host // This method should be called when mouse leaves the host.
// it returns true if something was changed or false otherwise // It returns true if the active handler was changed or false otherwise.
static bool clearActive(ClickHandlerHost *host = nullptr) { static bool clearActive(ClickHandlerHost *host = nullptr) {
if (host && _activeHost != host) { if (host && _activeHost != host) {
return false; return false;
@ -71,7 +86,7 @@ public:
return setActive(ClickHandlerPtr(), host); return setActive(ClickHandlerPtr(), host);
} }
// this method should be called on mouse pressed // This method should be called on mouse press event.
static void pressed() { static void pressed() {
unpressed(); unpressed();
if (!_active || !*_active) { if (!_active || !*_active) {
@ -84,8 +99,8 @@ public:
} }
} }
// this method should be called on mouse released // This method should be called on mouse release event.
// the activated click handler is returned // The activated click handler (if any) is returned.
static ClickHandlerPtr unpressed() { static ClickHandlerPtr unpressed() {
if (_pressed && *_pressed) { if (_pressed && *_pressed) {
bool activated = (_active && *_active == *_pressed); bool activated = (_active && *_active == *_pressed);
@ -136,8 +151,12 @@ public:
} }
} }
private: protected:
// For click handlers like mention or hashtag in getExpandedLinkTextWithEntities()
// we return just an empty string ("use original string part") with single entity.
TextWithEntities simpleTextWithEntity(const EntityInText &entity) const;
private:
static NeverFreedPointer<ClickHandlerPtr> _active; static NeverFreedPointer<ClickHandlerPtr> _active;
static NeverFreedPointer<ClickHandlerPtr> _pressed; static NeverFreedPointer<ClickHandlerPtr> _pressed;
static ClickHandlerHost *_activeHost; static ClickHandlerHost *_activeHost;

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "pspecific.h" #include "pspecific.h"
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
QString UrlClickHandler::copyToClipboardContextItem() const { QString UrlClickHandler::copyToClipboardContextItemText() const {
return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link); return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link);
} }
@ -74,8 +74,28 @@ void UrlClickHandler::doOpen(QString url) {
} }
} }
QString UrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const {
QString result;
if (mode != ExpandLinksNone) {
result = _originalUrl;
}
return result;
}
TextWithEntities UrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
TextWithEntities result;
auto entityType = isEmail(_originalUrl) ? EntityInTextEmail : EntityInTextUrl;
int entityLength = textPart.size();
if (mode != ExpandLinksNone) {
result.text = _originalUrl;
entityLength = _originalUrl.size();
}
result.entities.push_back({ entityType, entityOffset, entityLength });
return result;
}
void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const { void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const {
QString u = url(); auto u = url();
u = tryConvertUrlToLocal(u); u = tryConvertUrlToLocal(u);
@ -86,22 +106,24 @@ void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const {
} }
} }
QString LocationClickHandler::copyToClipboardContextItem() const { QString HiddenUrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const {
return lang(lng_context_copy_link); QString result;
if (mode == ExpandLinksAll) {
result = textPart.toString() + qsl(" (") + url() + ')';
}
return result;
} }
void LocationClickHandler::onClick(Qt::MouseButton button) const { TextWithEntities HiddenUrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
if (!psLaunchMaps(_coords)) { TextWithEntities result;
QDesktopServices::openUrl(_text); result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() });
if (mode == ExpandLinksAll) {
result.text = textPart.toString() + qsl(" (") + url() + ')';
} }
return result;
} }
void LocationClickHandler::setup() { QString MentionClickHandler::copyToClipboardContextItemText() const {
QString latlon(qsl("%1,%2").arg(_coords.lat).arg(_coords.lon));
_text = qsl("https://maps.google.com/maps?q=") + latlon + qsl("&ll=") + latlon + qsl("&z=16");
}
QString MentionClickHandler::copyToClipboardContextItem() const {
return lang(lng_context_copy_mention); return lang(lng_context_copy_mention);
} }
@ -111,7 +133,34 @@ void MentionClickHandler::onClick(Qt::MouseButton button) const {
} }
} }
QString HashtagClickHandler::copyToClipboardContextItem() const { TextWithEntities MentionClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
return simpleTextWithEntity({ EntityInTextMention, entityOffset, textPart.size() });
}
void MentionNameClickHandler::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
if (auto user = App::userLoaded(_userId)) {
Ui::showPeerProfile(user);
}
}
}
TextWithEntities MentionNameClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
auto data = QString::number(_userId) + '.' + QString::number(_accessHash);
return simpleTextWithEntity({ EntityInTextMentionName, entityOffset, textPart.size(), data });
}
QString MentionNameClickHandler::tooltip() const {
if (auto user = App::userLoaded(_userId)) {
auto name = App::peerName(user);
if (name != _text) {
return name;
}
}
return QString();
}
QString HashtagClickHandler::copyToClipboardContextItemText() const {
return lang(lng_context_copy_hashtag); return lang(lng_context_copy_hashtag);
} }
@ -121,6 +170,10 @@ void HashtagClickHandler::onClick(Qt::MouseButton button) const {
} }
} }
TextWithEntities HashtagClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
}
void BotCommandClickHandler::onClick(Qt::MouseButton button) const { void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) { if (button == Qt::LeftButton || button == Qt::MiddleButton) {
if (PeerData *peer = Ui::getPeerForMouseAction()) { if (PeerData *peer = Ui::getPeerForMouseAction()) {
@ -137,3 +190,7 @@ void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
} }
} }
} }
TextWithEntities BotCommandClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
}

View File

@ -55,23 +55,23 @@ protected:
class UrlClickHandler : public TextClickHandler { class UrlClickHandler : public TextClickHandler {
public: public:
UrlClickHandler(const QString &url, bool fullDisplayed = true) : TextClickHandler(fullDisplayed), _url(url) { UrlClickHandler(const QString &url, bool fullDisplayed = true) : TextClickHandler(fullDisplayed), _originalUrl(url) {
if (isEmail()) { if (isEmail()) {
_readable = _url; _readable = _originalUrl;
} else { } else {
QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString());
_readable = good.isValid() ? good.toDisplayString() : _url; _readable = good.isValid() ? good.toDisplayString() : _originalUrl;
} }
} }
QString copyToClipboardContextItem() const override; QString copyToClipboardContextItemText() const override;
QString text() const override {
return _url;
}
QString dragText() const override { QString dragText() const override {
return url(); return url();
} }
QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
static void doOpen(QString url); static void doOpen(QString url);
void onClick(Qt::MouseButton button) const override { void onClick(Qt::MouseButton button) const override {
if (button == Qt::LeftButton || button == Qt::MiddleButton) { if (button == Qt::LeftButton || button == Qt::MiddleButton) {
@ -82,11 +82,11 @@ public:
protected: protected:
QString url() const override { QString url() const override {
if (isEmail()) { if (isEmail()) {
return _url; return _originalUrl;
} }
QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); QUrl u(_originalUrl), good(u.isValid() ? u.toEncoded() : QString());
QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _url); QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _originalUrl);
if (!QRegularExpression(qsl("^[a-zA-Z]+:")).match(result).hasMatch()) { // no protocol if (!QRegularExpression(qsl("^[a-zA-Z]+:")).match(result).hasMatch()) { // no protocol
return qsl("http://") + result; return qsl("http://") + result;
@ -103,10 +103,10 @@ private:
return ((at > 0) && (slash < 0 || slash > at)); return ((at > 0) && (slash < 0 || slash > at));
} }
bool isEmail() const { bool isEmail() const {
return isEmail(_url); return isEmail(_originalUrl);
} }
QString _url, _readable; QString _originalUrl, _readable;
}; };
typedef QSharedPointer<TextClickHandler> TextClickHandlerPtr; typedef QSharedPointer<TextClickHandler> TextClickHandlerPtr;
@ -117,18 +117,25 @@ public:
} }
void onClick(Qt::MouseButton button) const override; void onClick(Qt::MouseButton button) const override;
QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
}; };
class MentionClickHandler : public TextClickHandler { class MentionClickHandler : public TextClickHandler {
public: public:
MentionClickHandler(const QString &tag) : _tag(tag) { MentionClickHandler(const QString &tag) : _tag(tag) {
} }
QString copyToClipboardContextItem() const override;
QString text() const override { void onClick(Qt::MouseButton button) const override;
QString dragText() const override {
return _tag; return _tag;
} }
void onClick(Qt::MouseButton button) const override;
QString copyToClipboardContextItemText() const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
protected: protected:
QString url() const override { QString url() const override {
@ -140,16 +147,41 @@ private:
}; };
class MentionNameClickHandler : public ClickHandler {
public:
MentionNameClickHandler(QString text, UserId userId, uint64 accessHash)
: _text(text)
, _userId(userId)
, _accessHash(accessHash) {
}
void onClick(Qt::MouseButton button) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
QString tooltip() const override;
private:
QString _text;
UserId _userId;
uint64 _accessHash;
};
class HashtagClickHandler : public TextClickHandler { class HashtagClickHandler : public TextClickHandler {
public: public:
HashtagClickHandler(const QString &tag) : _tag(tag) { HashtagClickHandler(const QString &tag) : _tag(tag) {
} }
QString copyToClipboardContextItem() const override;
QString text() const override { void onClick(Qt::MouseButton button) const override;
QString dragText() const override {
return _tag; return _tag;
} }
void onClick(Qt::MouseButton button) const override;
QString copyToClipboardContextItemText() const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
protected: protected:
QString url() const override { QString url() const override {
@ -165,10 +197,14 @@ class BotCommandClickHandler : public TextClickHandler {
public: public:
BotCommandClickHandler(const QString &cmd) : _cmd(cmd) { BotCommandClickHandler(const QString &cmd) : _cmd(cmd) {
} }
QString text() const override {
void onClick(Qt::MouseButton button) const override;
QString dragText() const override {
return _cmd; return _cmd;
} }
void onClick(Qt::MouseButton button) const override;
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
protected: protected:
QString url() const override { QString url() const override {

View File

@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#define BETA_VERSION_MACRO (0ULL) #define BETA_VERSION_MACRO (0ULL)
constexpr int AppVersion = 9048; constexpr int AppVersion = 9049;
constexpr str_const AppVersionStr = "0.9.48"; constexpr str_const AppVersionStr = "0.9.49";
constexpr bool AppAlphaVersion = false; constexpr bool AppAlphaVersion = false;
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO; constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;

View File

@ -1395,8 +1395,8 @@ void DialogsInner::selectSkipPage(int32 pixels, int32 direction) {
_sel = *i; _sel = *i;
} }
} else { } else {
for (auto i = shownDialogs()->cfind(_sel), b = shownDialogs()->cbegin(); i != b && (toSkip--); --i) { for (auto i = shownDialogs()->cfind(_sel), b = shownDialogs()->cbegin(); i != b && (toSkip--);) {
_sel = *i; _sel = *(--i);
} }
if (toSkip && importantDialogs) { if (toSkip && importantDialogs) {
_importantSwitchSel = true; _importantSwitchSel = true;

View File

@ -3870,874 +3870,3 @@ void EmojiPan::recountContentMaxHeight() {
} }
updateContentHeight(); updateContentHeight();
} }
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows)
: _parent(parent)
, _mrows(mrows)
, _hrows(hrows)
, _brows(brows)
, _srows(srows)
, _stickersPerRow(1)
, _recentInlineBotsInRows(0)
, _sel(-1)
, _down(-1)
, _mouseSel(false)
, _overDelete(false)
, _previewShown(false) {
_previewTimer.setSingleShot(true);
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview()));
}
void MentionsInner::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect());
if (r != rect()) p.setClipRect(r);
int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#');
int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize;
int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right();
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
for (int32 row = fromrow; row < torow; ++row) {
for (int32 col = fromcol; col < tocol; ++col) {
int32 index = row * _stickersPerRow + col;
if (index >= _srows->size()) break;
DocumentData *sticker = _srows->at(index);
if (!sticker->sticker()) continue;
QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height());
if (_sel == index) {
QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width());
App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
}
bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128));
if (goodThumb) {
sticker->thumb->load();
} else {
sticker->checkSticker();
}
float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height()));
if (coef > 1) coef = 1;
int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height());
if (w < 1) w = 1;
if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
if (goodThumb) {
p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h));
} else if (!sticker->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h));
}
}
}
} else {
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
bool hasUsername = _parent->filter().indexOf('@') > 1;
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
bool selected = (i == _sel);
if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) {
p.drawSprite(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), st::notifyClose.icon);
}
}
p.setPen(st::black->p);
if (!_mrows->isEmpty()) {
UserData *user = _mrows->at(i);
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->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (mentionwidth < unamewidth + namewidth) {
namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
unamewidth = mentionwidth - namewidth;
if (firstwidth < unamewidth + st::mentionFont->elidew) {
if (firstwidth < unamewidth) {
first = st::mentionFont->elided(first, unamewidth);
} else if (!second.isEmpty()) {
first = st::mentionFont->elided(first + second, unamewidth);
second = QString();
}
} else {
second = st::mentionFont->elided(second, unamewidth - firstwidth);
}
}
user->loadUserpic();
user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width());
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
p.setFont(st::mentionFont->f);
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else if (!_hrows->isEmpty()) {
QString hrow = _hrows->at(i);
QString first = (_parent->filter().size() < 2) ? QString() : ('#' + hrow.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('#' + hrow) : hrow.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (htagwidth < firstwidth + secondwidth) {
if (htagwidth < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, htagwidth);
second = QString();
} else {
second = st::mentionFont->elided(second, htagwidth - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else {
UserData *user = _brows->at(i).first;
const BotCommand *command = _brows->at(i).second;
QString toHighlight = command->command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) {
toHighlight += '@' + user->username;
}
user->loadUserpic();
user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width());
int32 addleft = 0, widthleft = mentionwidth;
QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (widthleft < firstwidth + secondwidth) {
if (widthleft < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, widthleft);
second = QString();
} else {
second = st::mentionFont->elided(second, widthleft - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
addleft += firstwidth + secondwidth + st::mentionPadding.left();
widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft, 1, style::al_right);
}
}
}
p.fillRect(Adaptive::OneColumn() ? 0 : st::lineWidth, _parent->innerBottom() - st::lineWidth, width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::lineWidth, st::shadowColor->b);
}
p.fillRect(Adaptive::OneColumn() ? 0 : st::lineWidth, _parent->innerTop(), width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::lineWidth, st::shadowColor->b);
}
void MentionsInner::resizeEvent(QResizeEvent *e) {
_stickersPerRow = qMax(1, int32(width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
}
void MentionsInner::mouseMoveEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
}
void MentionsInner::clearSel(bool hidden) {
_mouseSel = _overDelete = false;
setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0);
if (hidden) {
_down = -1;
_previewShown = false;
}
}
bool MentionsInner::moveSel(int key) {
_mouseSel = false;
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size());
int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0);
if (!_srows->isEmpty()) {
if (key == Qt::Key_Left) {
direction = -1;
} else if (key == Qt::Key_Right) {
direction = 1;
} else {
direction *= _stickersPerRow;
}
}
if (_sel >= maxSel || _sel < 0) {
if (direction < -1) {
setSel(((maxSel - 1) / _stickersPerRow) * _stickersPerRow, true);
} else if (direction < 0) {
setSel(maxSel - 1, true);
} else {
setSel(0, true);
}
return (_sel >= 0 && _sel < maxSel);
}
setSel((_sel + direction >= maxSel || _sel + direction < 0) ? -1 : (_sel + direction), true);
return true;
}
bool MentionsInner::select() {
if (!_srows->isEmpty()) {
if (_sel >= 0 && _sel < _srows->size()) {
emit selected(_srows->at(_sel));
return true;
}
} else {
QString sel = getSelected();
if (!sel.isEmpty()) {
emit chosen(sel);
return true;
}
}
return false;
}
void MentionsInner::setRecentInlineBotsInRows(int32 bots) {
_recentInlineBotsInRows = bots;
}
QString MentionsInner::getSelected() const {
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size());
if (_sel >= 0 && _sel < maxSel) {
QString result;
if (!_mrows->isEmpty()) {
result = '@' + _mrows->at(_sel)->username;
} else if (!_hrows->isEmpty()) {
result = '#' + _hrows->at(_sel);
} else {
UserData *user = _brows->at(_sel).first;
const BotCommand *command(_brows->at(_sel).second);
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 1) {
result = '/' + command->command + '@' + user->username;
} else {
result = '/' + command->command;
}
}
return result;
}
return QString();
}
void MentionsInner::mousePressEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
if (e->button() == Qt::LeftButton) {
if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) {
_mousePos = mapToGlobal(e->pos());
bool removed = false;
if (_mrows->isEmpty()) {
QString toRemove = _hrows->at(_sel);
RecentHashtagPack &recent(cRefRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == toRemove) {
i = recent.erase(i);
removed = true;
} else {
++i;
}
}
} else {
UserData *toRemove = _mrows->at(_sel);
RecentInlineBots &recent(cRefRecentInlineBots());
int32 index = recent.indexOf(toRemove);
if (index >= 0) {
recent.remove(index);
removed = true;
}
}
if (removed) {
Local::writeRecentHashtagsAndBots();
}
_parent->updateFiltered();
_mouseSel = true;
onUpdateSelected(true);
} else if (_srows->isEmpty()) {
select();
} else {
_down = _sel;
_previewTimer.start(QApplication::startDragTime());
}
}
}
void MentionsInner::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.stop();
int32 pressed = _down;
_down = -1;
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
if (_previewShown) {
_previewShown = false;
return;
}
if (_sel < 0 || _sel != pressed || _srows->isEmpty()) return;
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::updateSelectedRow() {
if (_sel >= 0) {
if (_srows->isEmpty()) {
update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow, col = _sel % _stickersPerRow;
update(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanSize.width(), st::stickerPanSize.height());
}
}
}
void MentionsInner::setSel(int sel, bool scroll) {
updateSelectedRow();
_sel = sel;
updateSelectedRow();
if (scroll && _sel >= 0) {
if (_srows->isEmpty()) {
emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow;
emit mustScrollTo(st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanPadding + (row + 1) * st::stickerPanSize.height());
}
}
}
void MentionsInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(_mousePos));
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
if (_down >= 0 && !_previewShown) return;
int32 sel = -1, maxSel = 0;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 row = (mouse.y() >= st::stickerPanPadding) ? ((mouse.y() - st::stickerPanPadding) / st::stickerPanSize.height()) : -1;
int32 col = (mouse.x() >= st::stickerPanPadding) ? ((mouse.x() - st::stickerPanPadding) / st::stickerPanSize.width()) : -1;
if (row >= 0 && col >= 0) {
sel = row * _stickersPerRow + col;
}
maxSel = _srows->size();
_overDelete = false;
} else {
sel = mouse.y() / int32(st::mentionHeight);
maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false;
}
if (sel < 0 || sel >= maxSel) {
sel = -1;
}
if (sel != _sel) {
setSel(sel);
if (_down >= 0 && _sel >= 0 && _down != _sel) {
_down = _sel;
if (_down >= 0 && _down < _srows->size()) {
Ui::showMediaPreview(_srows->at(_down));
}
}
}
}
void MentionsInner::onParentGeometryChanged() {
_mousePos = QCursor::pos();
if (rect().contains(mapFromGlobal(_mousePos))) {
setMouseTracking(true);
onUpdateSelected(true);
}
}
void MentionsInner::onPreview() {
if (_down >= 0 && _down < _srows->size()) {
Ui::showMediaPreview(_srows->at(_down));
_previewShown = true;
}
}
MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent)
, _scroll(this, st::mentionScroll)
, _inner(this, &_mrows, &_hrows, &_brows, &_srows)
, _chat(0)
, _user(0)
, _channel(0)
, _hiding(false)
, a_opacity(0)
, _a_appearance(animation(this, &MentionsDropdown::step_appearance))
, _shadow(st::dropdownDef.shadow) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString)));
connect(&_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
connect(App::wnd(), SIGNAL(imageLoaded()), &_inner, SLOT(update()));
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()));
}
void MentionsDropdown::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
p.fillRect(rect(), st::white);
}
void MentionsDropdown::showFiltered(PeerData *peer, QString query, bool start) {
_chat = peer->asChat();
_user = peer->asUser();
_channel = peer->asChannel();
if (query.isEmpty()) {
rowsUpdated(MentionRows(), HashtagRows(), BotCommandRows(), _srows, false);
return;
}
_emoji = EmojiPtr();
query = query.toLower();
bool resetScroll = (_filter != query);
if (resetScroll) {
_filter = query;
}
_addInlineBots = start;
updateFiltered(resetScroll);
}
void MentionsDropdown::showStickers(EmojiPtr emoji) {
bool resetScroll = (_emoji != emoji);
_emoji = emoji;
if (!emoji) {
rowsUpdated(_mrows, _hrows, _brows, StickerPack(), false);
return;
}
_chat = 0;
_user = 0;
_channel = 0;
updateFiltered(resetScroll);
}
bool MentionsDropdown::clearFilteredBotCommands() {
if (_brows.isEmpty()) return false;
_brows.clear();
return true;
}
namespace {
template <typename T, typename U>
inline int indexOfInFirstN(const T &v, const U &elem, int last) {
for (auto b = v.cbegin(), i = b, e = b + qMax(v.size(), last); i != e; ++i) {
if (*i == elem) {
return (i - b);
}
}
return -1;
}
}
void MentionsDropdown::updateFiltered(bool resetScroll) {
int32 now = unixtime(), recentInlineBots = 0;
MentionRows mrows;
HashtagRows hrows;
BotCommandRows brows;
StickerPack srows;
if (_emoji) {
QMap<uint64, uint64> setsToRequest;
Stickers::Sets &sets(Global::RefStickerSets());
const Stickers::Order &order(Global::StickerSetsOrder());
for (int i = 0, l = order.size(); i < l; ++i) {
auto it = sets.find(order.at(i));
if (it != sets.cend()) {
if (it->emoji.isEmpty()) {
setsToRequest.insert(it->id, it->access);
it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded;
} else if (!(it->flags & MTPDstickerSet::Flag::f_disabled)) {
StickersByEmojiMap::const_iterator i = it->emoji.constFind(emojiGetNoColor(_emoji));
if (i != it->emoji.cend()) {
srows += *i;
}
}
}
}
if (!setsToRequest.isEmpty() && App::api()) {
for (QMap<uint64, uint64>::const_iterator i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
App::api()->scheduleStickerSetRequest(i.key(), i.value());
}
App::api()->requestStickerSets();
}
} else if (_filter.at(0) == '@') {
if (_chat) {
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
} else {
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + _channel->mgInfo->lastParticipants.size());
}
} else if (_addInlineBots) {
mrows.reserve(cRecentInlineBots().size());
}
if (_addInlineBots) {
for (RecentInlineBots::const_iterator i = cRecentInlineBots().cbegin(), e = cRecentInlineBots().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;
mrows.push_back(user);
++recentInlineBots;
}
}
if (_chat) {
QMultiMap<int32, UserData*> ordered;
mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
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;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
ordered.insertMulti(App::onlineForSort(user, 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;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user, now), user);
}
}
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
mrows.push_back(i.value());
}
}
} else if (_channel && _channel->isMegagroup()) {
QMultiMap<int32, UserData*> ordered;
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_channel);
} else {
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.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;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user);
}
}
}
} else if (_filter.at(0) == '#') {
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);
}
} else if (_filter.at(0) == '/') {
bool hasUsername = _filter.indexOf('@') > 1;
QMap<UserData*, bool> bots;
int32 cnt = 0;
if (_chat) {
if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (!user->botInfo) continue;
if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user);
if (user->botInfo->commands.isEmpty()) continue;
bots.insert(user, true);
cnt += user->botInfo->commands.size();
}
}
} else if (_user && _user->botInfo) {
if (!_user->botInfo->inited && App::api()) App::api()->requestFullPeer(_user);
cnt = _user->botInfo->commands.size();
bots.insert(_user, true);
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->bots.isEmpty()) {
if (!_channel->mgInfo->botStatus && App::api()) App::api()->requestBots(_channel);
} else {
for_const (auto user, _channel->mgInfo->bots) {
if (!user->botInfo) continue;
if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user);
if (user->botInfo->commands.isEmpty()) continue;
bots.insert(user, true);
cnt += user->botInfo->commands.size();
}
}
}
if (cnt) {
brows.reserve(cnt);
int32 botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : -1);
if (_chat) {
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (!user->botInfo) continue;
if (!bots.contains(user)) continue;
if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user);
if (user->botInfo->commands.isEmpty()) continue;
bots.remove(user);
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (_filter.size() > 1) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive)/* || toFilter.size() + 1 == _filter.size()*/) continue;
}
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
}
}
if (!bots.isEmpty()) {
for (QMap<UserData*, bool>::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
UserData *user = i.key();
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (_filter.size() > 1) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive)/* || toFilter.size() + 1 == _filter.size()*/) continue;
}
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
}
}
}
}
rowsUpdated(mrows, hrows, brows, srows, resetScroll);
_inner.setRecentInlineBotsInRows(recentInlineBots);
}
void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerPack &srows, bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) {
if (!isHidden()) {
hideStart();
}
_mrows.clear();
_hrows.clear();
_brows.clear();
_srows.clear();
} else {
_mrows = mrows;
_hrows = hrows;
_brows = brows;
_srows = srows;
bool hidden = _hiding || isHidden();
if (hidden) {
show();
_scroll.show();
}
recount(resetScroll);
update();
if (hidden) {
hide();
showStart();
}
}
}
void MentionsDropdown::setBoundings(QRect boundings) {
_boundings = boundings;
recount();
}
void MentionsDropdown::recount(bool resetScroll) {
int32 h = 0, oldst = _scroll.scrollTop(), st = oldst, maxh = 4.5 * st::mentionHeight;
if (!_srows.isEmpty()) {
int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
int32 rows = rowscount(_srows.size(), stickersPerRow);
h = st::stickerPanPadding + rows * st::stickerPanSize.height();
} else if (!_mrows.isEmpty()) {
h = _mrows.size() * st::mentionHeight;
} else if (!_hrows.isEmpty()) {
h = _hrows.size() * st::mentionHeight;
} else if (!_brows.isEmpty()) {
h = _brows.size() * st::mentionHeight;
}
if (_inner.width() != _boundings.width() || _inner.height() != h) {
_inner.resize(_boundings.width(), h);
}
if (h > _boundings.height()) h = _boundings.height();
if (h > maxh) h = maxh;
if (width() != _boundings.width() || height() != h) {
setGeometry(_boundings.x(), _boundings.y() + _boundings.height() - h, _boundings.width(), h);
_scroll.resize(_boundings.width(), h);
} else if (y() != _boundings.y() + _boundings.height() - h) {
move(_boundings.x(), _boundings.y() + _boundings.height() - h);
}
if (resetScroll) st = 0;
if (st != oldst) _scroll.scrollToY(st);
if (resetScroll) _inner.clearSel();
}
void MentionsDropdown::fastHide() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
hideFinish();
}
void MentionsDropdown::hideStart() {
if (!_hiding) {
if (_cache.isNull()) {
_scroll.show();
_cache = myGrab(this);
}
_scroll.hide();
_hiding = true;
a_opacity.start(0);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
}
void MentionsDropdown::hideFinish() {
hide();
_hiding = false;
_filter = qsl("-");
_inner.clearSel(true);
}
void MentionsDropdown::showStart() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) {
return;
}
if (_cache.isNull()) {
_scroll.show();
_cache = myGrab(this);
}
_scroll.hide();
_hiding = false;
show();
a_opacity.start(1);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
void MentionsDropdown::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::dropdownDef.duration;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
_cache = QPixmap();
setAttribute(Qt::WA_OpaquePaintEvent);
if (_hiding) {
hideFinish();
} else {
_scroll.show();
_inner.clearSel();
}
} else {
a_opacity.update(dt, anim::linear);
}
if (timer) update();
}
const QString &MentionsDropdown::filter() const {
return _filter;
}
ChatData *MentionsDropdown::chat() const {
return _chat;
}
ChannelData *MentionsDropdown::channel() const {
return _channel;
}
UserData *MentionsDropdown::user() const {
return _user;
}
int32 MentionsDropdown::innerTop() {
return _scroll.scrollTop();
}
int32 MentionsDropdown::innerBottom() {
return _scroll.scrollTop() + _scroll.height();
}
QString MentionsDropdown::getSelected() const {
return _inner.getSelected();
}
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->modifiers() & (Qt::AltModifier | Qt::ControlModifier | Qt::ShiftModifier | Qt::MetaModifier))) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
return _inner.moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner.select();
}
}
}
return QWidget::eventFilter(obj, e);
}
MentionsDropdown::~MentionsDropdown() {
}

View File

@ -740,156 +740,3 @@ private:
bool inlineResultsFail(const RPCError &error); bool inlineResultsFail(const RPCError &error);
}; };
typedef QList<UserData*> MentionRows;
typedef QList<QString> HashtagRows;
typedef QList<QPair<UserData*, const BotCommand*> > BotCommandRows;
class MentionsDropdown;
class MentionsInner : public TWidget {
Q_OBJECT
public:
MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void clearSel(bool hidden = false);
bool moveSel(int key);
bool select();
void setRecentInlineBotsInRows(int32 bots);
QString getSelected() const;
signals:
void chosen(QString mentionOrHashtag);
void selected(DocumentData *sticker);
void mustScrollTo(int scrollToTop, int scrollToBottom);
public slots:
void onParentGeometryChanged();
void onUpdateSelected(bool force = false);
void onPreview();
private:
void updateSelectedRow();
void setSel(int sel, bool scroll = false);
MentionsDropdown *_parent;
MentionRows *_mrows;
HashtagRows *_hrows;
BotCommandRows *_brows;
StickerPack *_srows;
int32 _stickersPerRow, _recentInlineBotsInRows;
int32 _sel, _down;
bool _mouseSel;
QPoint _mousePos;
bool _overDelete;
bool _previewShown;
QTimer _previewTimer;
};
class MentionsDropdown : public TWidget {
Q_OBJECT
public:
MentionsDropdown(QWidget *parent);
void paintEvent(QPaintEvent *e);
void fastHide();
bool clearFilteredBotCommands();
void showFiltered(PeerData *peer, QString query, bool start);
void showStickers(EmojiPtr emoji);
void updateFiltered(bool resetScroll = false);
void setBoundings(QRect boundings);
void step_appearance(float64 ms, bool timer);
const QString &filter() const;
ChatData *chat() const;
ChannelData *channel() const;
UserData *user() const;
int32 innerTop();
int32 innerBottom();
bool eventFilter(QObject *obj, QEvent *e);
QString getSelected() const;
bool stickersShown() const {
return !_srows.isEmpty();
}
bool overlaps(const QRect &globalRect) {
if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false;
return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
~MentionsDropdown();
signals:
void chosen(QString mentionOrHashtag);
void stickerSelected(DocumentData *sticker);
public slots:
void hideStart();
void hideFinish();
void showStart();
private:
void recount(bool resetScroll = false);
QPixmap _cache;
MentionRows _mrows;
HashtagRows _hrows;
BotCommandRows _brows;
StickerPack _srows;
void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerPack &srows, bool resetScroll);
ScrollArea _scroll;
MentionsInner _inner;
ChatData *_chat;
UserData *_user;
ChannelData *_channel;
EmojiPtr _emoji;
QString _filter;
QRect _boundings;
bool _addInlineBots;
int32 _width, _height;
bool _hiding;
anim::fvalue a_opacity;
Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
};

View File

@ -236,6 +236,10 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
} }
} }
void showPeerProfile(const PeerId &peer) {
if (MainWidget *m = App::main()) m->showPeerProfile(App::peer(peer));
}
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) { void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) {
if (MainWidget *m = App::main()) m->ui_showPeerHistory(peer, msgId, back); if (MainWidget *m = App::main()) m->ui_showPeerHistory(peer, msgId, back);
} }

View File

@ -67,6 +67,14 @@ void repaintInlineItem(const InlineBots::Layout::ItemBase *layout);
bool isInlineItemVisible(const InlineBots::Layout::ItemBase *reader); bool isInlineItemVisible(const InlineBots::Layout::ItemBase *reader);
void autoplayMediaInlineAsync(const FullMsgId &msgId); void autoplayMediaInlineAsync(const FullMsgId &msgId);
void showPeerProfile(const PeerId &peer);
inline void showPeerProfile(const PeerData *peer) {
showPeerProfile(peer->id);
}
inline void showPeerProfile(const History *history) {
showPeerProfile(history->peer->id);
}
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false); void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false);
inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) { inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) {
showPeerHistory(peer->id, msgId, back); showPeerHistory(peer->id, msgId, back);

View File

@ -1029,9 +1029,10 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
} }
if (badMedia == 1) { if (badMedia == 1) {
QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org"))); QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")));
EntitiesInText entities = textParseEntities(text, _historyTextNoMonoOptions.flags); EntitiesInText entities;
textParseEntities(text, _historyTextNoMonoOptions.flags, &entities);
entities.push_front(EntityInText(EntityInTextItalic, 0, text.size())); entities.push_front(EntityInText(EntityInTextItalic, 0, text.size()));
result = HistoryMessage::create(this, m.vid.v, m.vflags.v, m.vreply_to_msg_id.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v, text, entities); result = HistoryMessage::create(this, m.vid.v, m.vflags.v, m.vreply_to_msg_id.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v, { text, entities });
} else if (badMedia) { } else if (badMedia) {
result = HistoryService::create(this, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0); result = HistoryService::create(this, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0);
} else { } else {
@ -3094,26 +3095,25 @@ void HistoryItem::setId(MsgId newId) {
} }
bool HistoryItem::canEdit(const QDateTime &cur) const { bool HistoryItem::canEdit(const QDateTime &cur) const {
int32 s = date.secsTo(cur); if (id < 0 || date.secsTo(cur) >= Global::EditTimeLimit()) return false;
auto channel = _history->peer->asChannel();
if (!channel || id < 0 || date.secsTo(cur) >= Global::EditTimeLimit()) return false;
if (const HistoryMessage *msg = toHistoryMessage()) { if (auto msg = toHistoryMessage()) {
if (msg->Has<HistoryMessageVia>() || msg->Has<HistoryMessageForwarded>()) return false; if (msg->Has<HistoryMessageVia>() || msg->Has<HistoryMessageForwarded>()) return false;
if (HistoryMedia *media = msg->getMedia()) { if (HistoryMedia *media = msg->getMedia()) {
HistoryMediaType t = media->type(); auto type = media->type();
if (t != MediaTypePhoto && if (type != MediaTypePhoto &&
t != MediaTypeVideo && type != MediaTypeVideo &&
t != MediaTypeFile && type != MediaTypeFile &&
t != MediaTypeGif && type != MediaTypeGif &&
t != MediaTypeMusicFile && type != MediaTypeMusicFile &&
t != MediaTypeVoiceFile && type != MediaTypeVoiceFile &&
t != MediaTypeWebPage) { type != MediaTypeWebPage) {
return false; return false;
} }
} }
if (isPost()) { if (isPost()) {
auto channel = _history->peer->asChannel();
return (channel->amCreator() || (channel->amEditor() && out())); return (channel->amCreator() || (channel->amEditor() && out()));
} }
return out(); return out();
@ -3303,18 +3303,24 @@ int32 gifMaxStatusWidth(DocumentData *document) {
return result; return result;
} }
QString captionedSelectedText(const QString &attachType, const Text &caption, TextSelection selection) { TextWithEntities captionedSelectedText(const QString &attachType, const Text &caption, TextSelection selection) {
if (selection != FullSelection) { if (selection != FullSelection) {
return caption.original(selection, Text::ExpandLinksAll); return caption.originalTextWithEntities(selection, ExpandLinksAll);
} }
QString result;
result.reserve(5 + attachType.size() + caption.length()); TextWithEntities result, original;
result.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
if (!caption.isEmpty()) { if (!caption.isEmpty()) {
result.append(qstr("\n")).append(caption.original(AllTextSelection)); original = caption.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
}
result.text.reserve(5 + attachType.size() + original.text.size());
result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
if (!caption.isEmpty()) {
result.text.append(qstr("\n"));
appendTextWithEntities(result, std_::move(original));
} }
return result; return result;
} }
} // namespace } // namespace
void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
@ -3734,10 +3740,10 @@ void HistoryPhoto::detachFromParent() {
} }
QString HistoryPhoto::inDialogsText() const { QString HistoryPhoto::inDialogsText() const {
return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(AllTextSelection, Text::ExpandLinksNone); return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.originalText(AllTextSelection, ExpandLinksNone);
} }
QString HistoryPhoto::selectedText(TextSelection selection) const { TextWithEntities HistoryPhoto::selectedText(TextSelection selection) const {
return captionedSelectedText(lang(lng_in_dlg_photo), _caption, selection); return captionedSelectedText(lang(lng_in_dlg_photo), _caption, selection);
} }
@ -3980,10 +3986,10 @@ void HistoryVideo::setStatusSize(int32 newSize) const {
} }
QString HistoryVideo::inDialogsText() const { QString HistoryVideo::inDialogsText() const {
return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(AllTextSelection, Text::ExpandLinksNone); return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.originalText(AllTextSelection, ExpandLinksNone);
} }
QString HistoryVideo::selectedText(TextSelection selection) const { TextWithEntities HistoryVideo::selectedText(TextSelection selection) const {
return captionedSelectedText(lang(lng_in_dlg_video), _caption, selection); return captionedSelectedText(lang(lng_in_dlg_video), _caption, selection);
} }
@ -4471,13 +4477,13 @@ QString HistoryDocument::inDialogsText() const {
} }
if (auto captioned = Get<HistoryDocumentCaptioned>()) { if (auto captioned = Get<HistoryDocumentCaptioned>()) {
if (!captioned->_caption.isEmpty()) { if (!captioned->_caption.isEmpty()) {
result.append(' ').append(captioned->_caption.original(AllTextSelection, Text::ExpandLinksNone)); result.append(' ').append(captioned->_caption.originalText(AllTextSelection, ExpandLinksNone));
} }
} }
return result; return result;
} }
QString HistoryDocument::selectedText(TextSelection selection) const { TextWithEntities HistoryDocument::selectedText(TextSelection selection) const {
const Text emptyCaption; const Text emptyCaption;
const Text *caption = &emptyCaption; const Text *caption = &emptyCaption;
if (auto captioned = Get<HistoryDocumentCaptioned>()) { if (auto captioned = Get<HistoryDocumentCaptioned>()) {
@ -4929,10 +4935,10 @@ HistoryTextState HistoryGif::getState(int x, int y, HistoryStateRequest request)
} }
QString HistoryGif::inDialogsText() const { QString HistoryGif::inDialogsText() const {
return qsl("GIF") + (_caption.isEmpty() ? QString() : (' ' + _caption.original(AllTextSelection, Text::ExpandLinksNone))); return qsl("GIF") + (_caption.isEmpty() ? QString() : (' ' + _caption.originalText(AllTextSelection, ExpandLinksNone)));
} }
QString HistoryGif::selectedText(TextSelection selection) const { TextWithEntities HistoryGif::selectedText(TextSelection selection) const {
return captionedSelectedText(qsl("GIF"), _caption, selection); return captionedSelectedText(qsl("GIF"), _caption, selection);
} }
@ -5249,11 +5255,11 @@ QString HistorySticker::inDialogsText() const {
return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji); return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji);
} }
QString HistorySticker::selectedText(TextSelection selection) const { TextWithEntities HistorySticker::selectedText(TextSelection selection) const {
if (selection != FullSelection) { if (selection != FullSelection) {
return QString(); return TextWithEntities();
} }
return qsl("[ ") + inDialogsText() + qsl(" ]"); return { qsl("[ ") + inDialogsText() + qsl(" ]"), EntitiesInText() };
} }
void HistorySticker::attachToParent() { void HistorySticker::attachToParent() {
@ -5350,6 +5356,9 @@ void HistoryContact::initDimensions() {
if (_userId) { if (_userId) {
_minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
if (_parent->Has<HistoryMessageSigned>()) {
_minh += st::msgDateFont->height - st::msgDateDelta.y();
}
} else { } else {
_minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
} }
@ -5434,11 +5443,11 @@ QString HistoryContact::inDialogsText() const {
return lang(lng_in_dlg_contact); return lang(lng_in_dlg_contact);
} }
QString HistoryContact::selectedText(TextSelection selection) const { TextWithEntities HistoryContact::selectedText(TextSelection selection) const {
if (selection != FullSelection) { if (selection != FullSelection) {
return QString(); return TextWithEntities();
} }
return qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" ]\n") + _name.original() + '\n' + _phone; return { qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" ]\n") + _name.originalText() + '\n' + _phone, EntitiesInText() };
} }
void HistoryContact::attachToParent() { void HistoryContact::attachToParent() {
@ -5955,18 +5964,21 @@ QString HistoryWebPage::inDialogsText() const {
return QString(); return QString();
} }
QString HistoryWebPage::selectedText(TextSelection selection) const { TextWithEntities HistoryWebPage::selectedText(TextSelection selection) const {
if (selection == FullSelection) { if (selection == FullSelection) {
return QString(); return TextWithEntities();
} }
auto titleResult = _title.original(selection, Text::ExpandLinksAll); auto titleResult = _title.originalTextWithEntities(selection, ExpandLinksAll);
auto descriptionResult = _description.original(toDescriptionSelection(selection), Text::ExpandLinksAll); auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection), ExpandLinksAll);
if (titleResult.isEmpty()) { if (titleResult.text.isEmpty()) {
return descriptionResult; return descriptionResult;
} else if (descriptionResult.isEmpty()) { } else if (descriptionResult.text.isEmpty()) {
return titleResult; return titleResult;
} }
return titleResult + '\n' + descriptionResult;
titleResult.text += '\n';
appendTextWithEntities(titleResult, std_::move(descriptionResult));
return titleResult;
} }
ImagePtr HistoryWebPage::replyPreview() { ImagePtr HistoryWebPage::replyPreview() {
@ -6398,24 +6410,31 @@ TextSelection HistoryLocation::adjustSelection(TextSelection selection, TextSele
} }
QString HistoryLocation::inDialogsText() const { QString HistoryLocation::inDialogsText() const {
return _title.isEmpty() ? lang(lng_maps_point) : _title.original(AllTextSelection); return _title.isEmpty() ? lang(lng_maps_point) : _title.originalText(AllTextSelection);
} }
QString HistoryLocation::selectedText(TextSelection selection) const { TextWithEntities HistoryLocation::selectedText(TextSelection selection) const {
if (selection == FullSelection) { if (selection == FullSelection) {
auto result = qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"); TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() };
auto info = selectedText(AllTextSelection); auto info = selectedText(AllTextSelection);
if (!info.isEmpty()) result.append(info).append('\n'); if (!info.text.isEmpty()) {
return result + _link->text(); appendTextWithEntities(result, std_::move(info));
result.text.append('\n');
} }
auto titleResult = _title.original(selection); result.text += _link->dragText();
auto descriptionResult = _description.original(toDescriptionSelection(selection)); return result;
if (titleResult.isEmpty()) { }
auto titleResult = _title.originalTextWithEntities(selection);
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection));
if (titleResult.text.isEmpty()) {
return descriptionResult; return descriptionResult;
} else if (descriptionResult.isEmpty()) { } else if (descriptionResult.text.isEmpty()) {
return titleResult; return titleResult;
} }
return titleResult + '\n' + descriptionResult; titleResult.text += '\n';
appendTextWithEntities(titleResult, std_::move(descriptionResult));
return titleResult;
} }
int32 HistoryLocation::fullWidth() const { int32 HistoryLocation::fullWidth() const {
@ -6468,7 +6487,7 @@ void HistoryMessageEdited::create(const QDateTime &editDate, const QDateTime &da
_editDate = editDate; _editDate = editDate;
QString time = date.toString(cTimeFormat()); QString time = date.toString(cTimeFormat());
_edited.setText(st::msgDateFont, time, _textNameOptions); _edited.setText(st::msgDateFont, lang(lng_edited) + ' ' + time, _textNameOptions);
} }
int HistoryMessageEdited::maxWidth() const { int HistoryMessageEdited::maxWidth() const {
@ -6727,9 +6746,41 @@ HistoryMessage::HistoryMessage(History *history, const MTPDmessage &msg)
createComponents(config); createComponents(config);
QString text(textClean(qs(msg.vmessage))); QString text(textClean(qs(msg.vmessage)));
initMedia(msg.has_media() ? (&msg.vmedia) : 0, text); initMedia(msg.has_media() ? (&msg.vmedia) : nullptr, text);
setText(text, msg.has_entities() ? entitiesFromMTP(msg.ventities.c_vector().v) : EntitiesInText());
TextWithEntities textWithEntities = { text, EntitiesInText() };
if (msg.has_entities()) {
textWithEntities.entities = entitiesFromMTP(msg.ventities.c_vector().v);
} }
setText(textWithEntities);
}
namespace {
MTPDmessage::Flags newForwardedFlags(PeerData *p, int32 from, HistoryMessage *fwd) {
MTPDmessage::Flags result = newMessageFlags(p) | MTPDmessage::Flag::f_fwd_from;
if (from) {
result |= MTPDmessage::Flag::f_from_id;
}
if (fwd->Has<HistoryMessageVia>()) {
result |= MTPDmessage::Flag::f_via_bot_id;
}
if (!p->isChannel()) {
if (HistoryMedia *media = fwd->getMedia()) {
if (media->type() == MediaTypeVoiceFile) {
result |= MTPDmessage::Flag::f_media_unread;
// } else if (media->type() == MediaTypeVideo) {
// result |= MTPDmessage::flag_media_unread;
}
}
}
if (fwd->hasViews()) {
result |= MTPDmessage::Flag::f_views;
}
return result;
}
} // namespace
HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd) HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd)
: HistoryItem(history, id, newForwardedFlags(history->peer, from, fwd) | flags, date, from) { : HistoryItem(history, id, newForwardedFlags(history->peer, from, fwd) | flags, date, from) {
@ -6754,14 +6805,14 @@ HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags fl
if (HistoryMedia *mediaOriginal = fwd->getMedia()) { if (HistoryMedia *mediaOriginal = fwd->getMedia()) {
_media.reset(mediaOriginal->clone(this)); _media.reset(mediaOriginal->clone(this));
} }
setText(fwd->originalText(), fwd->originalEntities()); setText(fwd->originalText());
} }
HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities) HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities)
: HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { : HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
createComponentsHelper(flags, replyTo, viaBotId, MTPnullMarkup); createComponentsHelper(flags, replyTo, viaBotId, MTPnullMarkup);
setText(msg, entities); setText(textWithEntities);
} }
HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup) HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup)
@ -6769,7 +6820,7 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags
createComponentsHelper(flags, replyTo, viaBotId, markup); createComponentsHelper(flags, replyTo, viaBotId, markup);
initMediaFromDocument(doc, caption); initMediaFromDocument(doc, caption);
setText(QString(), EntitiesInText()); setText(TextWithEntities());
} }
HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup) HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup)
@ -6777,7 +6828,7 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags
createComponentsHelper(flags, replyTo, viaBotId, markup); createComponentsHelper(flags, replyTo, viaBotId, markup);
_media.reset(new HistoryPhoto(this, photo, caption)); _media.reset(new HistoryPhoto(this, photo, caption));
setText(QString(), EntitiesInText()); setText(TextWithEntities());
} }
void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) { void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) {
@ -6822,7 +6873,7 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
if (auto reply = Get<HistoryMessageReply>()) { if (auto reply = Get<HistoryMessageReply>()) {
reply->replyToMsgId = config.replyTo; reply->replyToMsgId = config.replyTo;
if (!reply->updateData(this) && App::api()) { if (!reply->updateData(this) && App::api()) {
App::api()->requestMessageData(history()->peer->asChannel(), reply->replyToMsgId, new HistoryDependentItemCallback(fullId())); App::api()->requestMessageData(history()->peer->asChannel(), reply->replyToMsgId, std_::make_unique<HistoryDependentItemCallback>(fullId()));
} }
} }
if (auto via = Get<HistoryMessageVia>()) { if (auto via = Get<HistoryMessageVia>()) {
@ -6959,12 +7010,12 @@ void HistoryMessage::initDimensions() {
if (_media->isDisplayed()) { if (_media->isDisplayed()) {
if (_text.hasSkipBlock()) { if (_text.hasSkipBlock()) {
_text.removeSkipBlock(); _text.removeSkipBlock();
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} }
} else if (!_text.hasSkipBlock()) { } else if (!_text.hasSkipBlock()) {
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight()); _text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} }
} }
@ -7068,11 +7119,6 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
} }
} }
EntitiesInText entities;
if (message.has_entities()) {
entities = entitiesFromMTP(message.ventities.c_vector().v);
}
if (message.has_edit_date()) { if (message.has_edit_date()) {
_flags |= MTPDmessage::Flag::f_edit_date; _flags |= MTPDmessage::Flag::f_edit_date;
if (!Has<HistoryMessageEdited>()) { if (!Has<HistoryMessageEdited>()) {
@ -7082,7 +7128,11 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
initTime(); initTime();
} }
setText(qs(message.vmessage), entities); TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() };
if (message.has_entities()) {
textWithEntities.entities = entitiesFromMTP(message.ventities.c_vector().v);
}
setText(textWithEntities);
setMedia(message.has_media() ? (&message.vmedia) : nullptr); setMedia(message.has_media() ? (&message.vmedia) : nullptr);
setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr); setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr);
setViewsCount(message.has_views() ? message.vviews.v : -1); setViewsCount(message.has_views() ? message.vviews.v : -1);
@ -7102,6 +7152,8 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
keyboard->oldTop = keyboardTop; keyboard->oldTop = keyboardTop;
} }
} }
App::historyUpdateDependent(this);
} }
void HistoryMessage::updateMedia(const MTPMessageMedia *media) { void HistoryMessage::updateMedia(const MTPMessageMedia *media) {
@ -7154,36 +7206,44 @@ void HistoryMessage::eraseFromOverview() {
} }
} }
QString HistoryMessage::selectedText(TextSelection selection) const { TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
QString result, textResult, mediaResult; TextWithEntities result, textResult, mediaResult;
if (selection == FullSelection) { if (selection == FullSelection) {
textResult = _text.original(AllTextSelection, Text::ExpandLinksAll); textResult = _text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
} else { } else {
textResult = _text.original(selection, Text::ExpandLinksAll); textResult = _text.originalTextWithEntities(selection, ExpandLinksAll);
} }
if (_media) { if (_media) {
mediaResult = _media->selectedText(toMediaSelection(selection)); mediaResult = _media->selectedText(toMediaSelection(selection));
} }
if (textResult.isEmpty()) { if (textResult.text.isEmpty()) {
result = mediaResult; result = mediaResult;
} else if (mediaResult.isEmpty()) { } else if (mediaResult.text.isEmpty()) {
result = textResult; result = textResult;
} else { } else {
result = textResult + qstr("\n\n") + mediaResult; result.text = textResult.text + qstr("\n\n");
result.entities = textResult.entities;
appendTextWithEntities(result, std_::move(mediaResult));
} }
if (auto fwd = Get<HistoryMessageForwarded>()) { if (auto fwd = Get<HistoryMessageForwarded>()) {
if (selection == FullSelection) { if (selection == FullSelection) {
QString fwdinfo = fwd->_text.original(AllTextSelection, Text::ExpandLinksAll), wrapped; auto fwdinfo = fwd->_text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
wrapped.reserve(fwdinfo.size() + 4 + result.size()); TextWithEntities wrapped;
wrapped.append('[').append(fwdinfo).append(qsl("]\n")).append(result); wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size());
wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size());
wrapped.text.append('[');
appendTextWithEntities(wrapped, std_::move(fwdinfo));
wrapped.text.append(qsl("]\n"));
appendTextWithEntities(wrapped, std_::move(result));
result = wrapped; result = wrapped;
} }
} }
if (auto reply = Get<HistoryMessageReply>()) { if (auto reply = Get<HistoryMessageReply>()) {
if (selection == FullSelection && reply->replyToMsg) { if (selection == FullSelection && reply->replyToMsg) {
QString wrapped; TextWithEntities wrapped;
wrapped.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.size()); wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size());
wrapped.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n")).append(result); wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n"));
appendTextWithEntities(wrapped, std_::move(result));
result = wrapped; result = wrapped;
} }
} }
@ -7191,7 +7251,7 @@ QString HistoryMessage::selectedText(TextSelection selection) const {
} }
QString HistoryMessage::inDialogsText() const { QString HistoryMessage::inDialogsText() const {
return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.original(AllTextSelection, Text::ExpandLinksNone); return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.originalText(AllTextSelection, ExpandLinksNone);
} }
HistoryMedia *HistoryMessage::getMedia() const { HistoryMedia *HistoryMessage::getMedia() const {
@ -7210,31 +7270,32 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media) {
initMedia(media, t); initMedia(media, t);
if (_media && _media->isDisplayed() && !mediaWasDisplayed) { if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
_text.removeSkipBlock(); _text.removeSkipBlock();
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) { } else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight()); _text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} }
} }
void HistoryMessage::setText(const QString &text, const EntitiesInText &entities) { void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle)); textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
if (_media && _media->isDisplayed()) { if (_media && _media->isDisplayed()) {
_text.setMarkedText(st::msgFont, text, entities, itemTextOptions(this)); _text.setMarkedText(st::msgFont, textWithEntities, itemTextOptions(this));
} else { } else {
_text.setMarkedText(st::msgFont, text + skipBlock(), entities, itemTextOptions(this)); _text.setMarkedText(st::msgFont, { textWithEntities.text + skipBlock(), textWithEntities.entities }, itemTextOptions(this));
} }
textstyleRestore(); textstyleRestore();
for (int32 i = 0, l = entities.size(); i != l; ++i) { for_const (auto &entity, textWithEntities.entities) {
if (entities.at(i).type == EntityInTextUrl || entities.at(i).type == EntityInTextCustomUrl || entities.at(i).type == EntityInTextEmail) { auto type = entity.type();
if (type == EntityInTextUrl || type == EntityInTextCustomUrl || type == EntityInTextEmail) {
_flags |= MTPDmessage_ClientFlag::f_has_text_links; _flags |= MTPDmessage_ClientFlag::f_has_text_links;
break; break;
} }
} }
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} }
@ -7282,15 +7343,14 @@ void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
} }
} }
QString HistoryMessage::originalText() const { TextWithEntities HistoryMessage::originalText() const {
return emptyText() ? QString() : _text.original(); if (emptyText()) {
return { QString(), EntitiesInText() };
}
return _text.originalTextWithEntities();
} }
EntitiesInText HistoryMessage::originalEntities() const { bool HistoryMessage::textHasLinks() const {
return emptyText() ? EntitiesInText() : _text.originalEntities();
}
bool HistoryMessage::textHasLinks() {
return emptyText() ? false : _text.hasLinks(); return emptyText() ? false : _text.hasLinks();
} }
@ -7393,7 +7453,7 @@ void HistoryMessage::setViewsCount(int32 count) {
} else { } else {
if (_text.hasSkipBlock()) { if (_text.hasSkipBlock()) {
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} }
setPendingInitDimensions(); setPendingInitDimensions();
@ -7408,7 +7468,7 @@ void HistoryMessage::setId(MsgId newId) {
} else { } else {
if (_text.hasSkipBlock()) { if (_text.hasSkipBlock()) {
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
_textWidth = 0; _textWidth = -1;
_textHeight = 0; _textHeight = 0;
} }
setPendingInitDimensions(); setPendingInitDimensions();
@ -8077,7 +8137,6 @@ bool HistoryService::updatePinned(bool force) {
updatePinnedText(); updatePinnedText();
} }
if (force) { if (force) {
setPendingInitDimensions();
if (gotDependencyItem && App::wnd()) { if (gotDependencyItem && App::wnd()) {
App::wnd()->notifySettingGot(); App::wnd()->notifySettingGot();
} }
@ -8111,7 +8170,7 @@ bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) {
case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break; case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break;
} }
if (mediaText.isEmpty()) { if (mediaText.isEmpty()) {
QString original = pinned->msg->originalText(); QString original = pinned->msg->originalText().text;
int32 cutat = 0, limit = PinnedMessageTextLimit, size = original.size(); int32 cutat = 0, limit = PinnedMessageTextLimit, size = original.size();
for (; limit > 0;) { for (; limit > 0;) {
--limit; --limit;
@ -8163,7 +8222,7 @@ HistoryService::HistoryService(History *history, const MTPDmessageService &msg)
UpdateComponents(HistoryServicePinned::Bit()); UpdateComponents(HistoryServicePinned::Bit());
MsgId pinnedMsgId = Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v; MsgId pinnedMsgId = Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
if (!updatePinned() && App::api()) { if (!updatePinned() && App::api()) {
App::api()->requestMessageData(history->peer->asChannel(), pinnedMsgId, new HistoryDependentItemCallback(fullId())); App::api()->requestMessageData(history->peer->asChannel(), pinnedMsgId, std_::make_unique<HistoryDependentItemCallback>(fullId()));
} }
} }
setMessageByAction(msg.vaction); setMessageByAction(msg.vaction);
@ -8189,12 +8248,12 @@ void HistoryService::countPositionAndSize(int32 &left, int32 &width) const {
width = maxwidth - st::msgServiceMargin.left() - st::msgServiceMargin.left(); width = maxwidth - st::msgServiceMargin.left() - st::msgServiceMargin.left();
} }
QString HistoryService::selectedText(TextSelection selection) const { TextWithEntities HistoryService::selectedText(TextSelection selection) const {
return _text.original((selection == FullSelection) ? AllTextSelection : selection); return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection);
} }
QString HistoryService::inDialogsText() const { QString HistoryService::inDialogsText() const {
return _text.original(AllTextSelection, Text::ExpandLinksNone); return _text.originalText(AllTextSelection, ExpandLinksNone);
} }
QString HistoryService::inReplyText() const { QString HistoryService::inReplyText() const {
@ -8206,7 +8265,9 @@ void HistoryService::setServiceText(const QString &text) {
textstyleSet(&st::serviceTextStyle); textstyleSet(&st::serviceTextStyle);
_text.setText(st::msgServiceFont, text, _historySrvOptions); _text.setText(st::msgServiceFont, text, _historySrvOptions);
textstyleRestore(); textstyleRestore();
initDimensions(); setPendingInitDimensions();
_textWidth = -1;
_textHeight = 0;
} }
void HistoryService::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const { void HistoryService::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const {
@ -8376,7 +8437,7 @@ void HistoryService::drawInDialog(Painter &p, const QRect &r, bool act, const Hi
} }
QString HistoryService::notificationText() const { QString HistoryService::notificationText() const {
QString msg = _text.original(); QString msg = _text.originalText();
if (msg.size() > 0xFF) msg = msg.mid(0, 0xFF) + qsl("..."); if (msg.size() > 0xFF) msg = msg.mid(0, 0xFF) + qsl("...");
return msg; return msg;
} }

View File

@ -151,22 +151,23 @@ struct SendAction {
int32 progress; int32 progress;
}; };
using TextWithTags = FlatTextarea::TextWithTags;
struct HistoryDraft { struct HistoryDraft {
HistoryDraft() : msgId(0), previewCancelled(false) { HistoryDraft() : msgId(0), previewCancelled(false) {
} }
HistoryDraft(const QString &text, MsgId msgId, const MessageCursor &cursor, bool previewCancelled) HistoryDraft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled)
: text(text) : textWithTags(textWithTags)
, msgId(msgId) , msgId(msgId)
, cursor(cursor) , cursor(cursor)
, previewCancelled(previewCancelled) { , previewCancelled(previewCancelled) {
} }
HistoryDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled) HistoryDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled)
: text(field.getLastText()) : textWithTags(field.getTextWithTags())
, msgId(msgId) , msgId(msgId)
, cursor(field) , cursor(field)
, previewCancelled(previewCancelled) { , previewCancelled(previewCancelled) {
} }
QString text; TextWithTags textWithTags;
MsgId msgId; // replyToId for message draft, editMsgId for edit draft MsgId msgId; // replyToId for message draft, editMsgId for edit draft
MessageCursor cursor; MessageCursor cursor;
bool previewCancelled; bool previewCancelled;
@ -176,8 +177,8 @@ struct HistoryEditDraft : public HistoryDraft {
: HistoryDraft() : HistoryDraft()
, saveRequest(0) { , saveRequest(0) {
} }
HistoryEditDraft(const QString &text, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequest = 0) HistoryEditDraft(const TextWithTags &textWithTags, MsgId msgId, const MessageCursor &cursor, bool previewCancelled, mtpRequestId saveRequest = 0)
: HistoryDraft(text, msgId, cursor, previewCancelled) : HistoryDraft(textWithTags, msgId, cursor, previewCancelled)
, saveRequest(saveRequest) { , saveRequest(saveRequest) {
} }
HistoryEditDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequest = 0) HistoryEditDraft(const FlatTextarea &field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequest = 0)
@ -373,7 +374,7 @@ public:
} }
void takeMsgDraft(History *from) { void takeMsgDraft(History *from) {
if (auto &draft = from->_msgDraft) { if (auto &draft = from->_msgDraft) {
if (!draft->text.isEmpty() && !_msgDraft) { if (!draft->textWithTags.text.isEmpty() && !_msgDraft) {
_msgDraft = std_::move(draft); _msgDraft = std_::move(draft);
_msgDraft->msgId = 0; // edit and reply to drafts can't migrate _msgDraft->msgId = 0; // edit and reply to drafts can't migrate
} }
@ -1292,8 +1293,8 @@ public:
} }
virtual void previousItemChanged(); virtual void previousItemChanged();
virtual QString selectedText(TextSelection selection) const { virtual TextWithEntities selectedText(TextSelection selection) const {
return qsl("[-]"); return { qsl("[-]"), EntitiesInText() };
} }
virtual QString inDialogsText() const { virtual QString inDialogsText() const {
return qsl("-"); return qsl("-");
@ -1301,6 +1302,9 @@ public:
virtual QString inReplyText() const { virtual QString inReplyText() const {
return inDialogsText(); return inDialogsText();
} }
virtual TextWithEntities originalText() const {
return { QString(), EntitiesInText() };
}
virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const { virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
} }
@ -1362,15 +1366,9 @@ public:
virtual HistoryMedia *getMedia() const { virtual HistoryMedia *getMedia() const {
return nullptr; return nullptr;
} }
virtual void setText(const QString &text, const EntitiesInText &links) { virtual void setText(const TextWithEntities &textWithEntities) {
} }
virtual QString originalText() const { virtual bool textHasLinks() const {
return QString();
}
virtual EntitiesInText originalEntities() const {
return EntitiesInText();
}
virtual bool textHasLinks() {
return false; return false;
} }
@ -1526,8 +1524,8 @@ protected:
// to add required bits to the Composer mask // to add required bits to the Composer mask
// after that always use Has<HistoryMessageDate>() // after that always use Has<HistoryMessageDate>()
bool displayDate() const { bool displayDate() const {
if (HistoryItem *prev = previous()) { if (auto prev = previous()) {
return prev->date.date().day() != date.date().day(); return prev->date.date() != date.date();
} }
return true; return true;
} }
@ -1566,7 +1564,8 @@ protected:
} }
Text _text = { int(st::msgMinWidth) }; Text _text = { int(st::msgMinWidth) };
int32 _textWidth, _textHeight; int _textWidth = -1;
int _textHeight = 0;
HistoryMediaPtr _media; HistoryMediaPtr _media;
@ -1661,7 +1660,7 @@ public:
virtual HistoryMediaType type() const = 0; virtual HistoryMediaType type() const = 0;
virtual QString inDialogsText() const = 0; virtual QString inDialogsText() const = 0;
virtual QString selectedText(TextSelection selection) const = 0; virtual TextWithEntities selectedText(TextSelection selection) const = 0;
bool hasPoint(int x, int y) const { bool hasPoint(int x, int y) const {
return (x >= 0 && y >= 0 && x < _width && y < _height); return (x >= 0 && y >= 0 && x < _width && y < _height);
@ -1748,8 +1747,8 @@ public:
virtual ImagePtr replyPreview() { virtual ImagePtr replyPreview() {
return ImagePtr(); return ImagePtr();
} }
virtual QString getCaption() const { virtual TextWithEntities getCaption() const {
return QString(); return TextWithEntities();
} }
virtual bool needsBubble() const = 0; virtual bool needsBubble() const = 0;
virtual bool customInfoLayout() const = 0; virtual bool customInfoLayout() const = 0;
@ -1898,7 +1897,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
PhotoData *photo() const { PhotoData *photo() const {
return _data; return _data;
@ -1915,8 +1914,8 @@ public:
} }
ImagePtr replyPreview() override; ImagePtr replyPreview() override;
QString getCaption() const override { TextWithEntities getCaption() const override {
return _caption.original(); return _caption.originalTextWithEntities();
} }
bool needsBubble() const override { bool needsBubble() const override {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
@ -1978,7 +1977,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() override { DocumentData *getDocument() override {
return _data; return _data;
@ -1998,8 +1997,8 @@ public:
} }
ImagePtr replyPreview() override; ImagePtr replyPreview() override;
QString getCaption() const override { TextWithEntities getCaption() const override {
return _caption.original(); return _caption.originalTextWithEntities();
} }
bool needsBubble() const override { bool needsBubble() const override {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
@ -2101,7 +2100,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
bool uploading() const override { bool uploading() const override {
return _data->uploading(); return _data->uploading();
@ -2122,11 +2121,11 @@ public:
} }
ImagePtr replyPreview() override; ImagePtr replyPreview() override;
QString getCaption() const override { TextWithEntities getCaption() const override {
if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) { if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.original(); return captioned->_caption.originalTextWithEntities();
} }
return QString(); return TextWithEntities();
} }
bool needsBubble() const override { bool needsBubble() const override {
return true; return true;
@ -2188,7 +2187,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
bool uploading() const override { bool uploading() const override {
return _data->uploading(); return _data->uploading();
@ -2215,8 +2214,8 @@ public:
} }
ImagePtr replyPreview() override; ImagePtr replyPreview() override;
QString getCaption() const override { TextWithEntities getCaption() const override {
return _caption.original(); return _caption.originalTextWithEntities();
} }
bool needsBubble() const override { bool needsBubble() const override {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
@ -2286,7 +2285,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() override { DocumentData *getDocument() override {
return _data; return _data;
@ -2355,7 +2354,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void attachToParent() override; void attachToParent() override;
void detachFromParent() override; void detachFromParent() override;
@ -2423,7 +2422,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
@ -2557,7 +2556,7 @@ public:
} }
QString inDialogsText() const override; QString inDialogsText() const override;
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
bool needsBubble() const override { bool needsBubble() const override {
if (!_title.isEmpty() || !_description.isEmpty()) { if (!_title.isEmpty() || !_description.isEmpty()) {
@ -2611,8 +2610,8 @@ public:
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd) { static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd) {
return _create(history, msgId, flags, date, from, fwd); return _create(history, msgId, flags, date, from, fwd);
} }
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities) { static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities) {
return _create(history, msgId, flags, replyTo, viaBotId, date, from, msg, entities); return _create(history, msgId, flags, replyTo, viaBotId, date, from, textWithEntities);
} }
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup) { static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup) {
return _create(history, msgId, flags, replyTo, viaBotId, date, from, doc, caption, markup); return _create(history, msgId, flags, replyTo, viaBotId, date, from, doc, caption, markup);
@ -2683,13 +2682,12 @@ public:
int32 addToOverview(AddToOverviewMethod method) override; int32 addToOverview(AddToOverviewMethod method) override;
void eraseFromOverview(); void eraseFromOverview();
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
QString inDialogsText() const override; QString inDialogsText() const override;
HistoryMedia *getMedia() const override; HistoryMedia *getMedia() const override;
void setText(const QString &text, const EntitiesInText &entities) override; void setText(const TextWithEntities &textWithEntities) override;
QString originalText() const override; TextWithEntities originalText() const override;
EntitiesInText originalEntities() const override; bool textHasLinks() const override;
bool textHasLinks() override;
int32 infoWidth() const override { int32 infoWidth() const override {
int32 result = _timeWidth; int32 result = _timeWidth;
@ -2751,7 +2749,7 @@ private:
HistoryMessage(History *history, const MTPDmessage &msg); HistoryMessage(History *history, const MTPDmessage &msg);
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd); // local forwarded HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd); // local forwarded
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities); // local message HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities); // local message
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); // local photo HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); // local photo
friend class HistoryItemInstantiated<HistoryMessage>; friend class HistoryItemInstantiated<HistoryMessage>;
@ -2820,25 +2818,6 @@ inline MTPDmessage::Flags newMessageFlags(PeerData *p) {
} }
return result; return result;
} }
inline MTPDmessage::Flags newForwardedFlags(PeerData *p, int32 from, HistoryMessage *fwd) {
MTPDmessage::Flags result = newMessageFlags(p);
if (from) {
result |= MTPDmessage::Flag::f_from_id;
}
if (fwd->Has<HistoryMessageVia>()) {
result |= MTPDmessage::Flag::f_via_bot_id;
}
if (!p->isChannel()) {
if (HistoryMedia *media = fwd->getMedia()) {
if (media->type() == MediaTypeVoiceFile) {
result |= MTPDmessage::Flag::f_media_unread;
// } else if (media->type() == MediaTypeVideo) {
// result |= MTPDmessage::flag_media_unread;
}
}
}
return result;
}
struct HistoryServicePinned : public BaseComponent<HistoryServicePinned> { struct HistoryServicePinned : public BaseComponent<HistoryServicePinned> {
MsgId msgId = 0; MsgId msgId = 0;
@ -2900,7 +2879,7 @@ public:
bool serviceMsg() const override { bool serviceMsg() const override {
return true; return true;
} }
QString selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
QString inDialogsText() const override; QString inDialogsText() const override;
QString inReplyText() const override; QString inReplyText() const override;
@ -2937,8 +2916,8 @@ public:
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override; HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
QString selectedText(TextSelection selection) const override { TextWithEntities selectedText(TextSelection selection) const override {
return QString(); return { QString(), EntitiesInText() };
} }
HistoryItemType type() const override { HistoryItemType type() const override {
return HistoryItemGroup; return HistoryItemGroup;
@ -2987,8 +2966,8 @@ public:
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override; void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override; HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
QString selectedText(TextSelection selection) const override { TextWithEntities selectedText(TextSelection selection) const override {
return QString(); return { QString(), EntitiesInText() };
} }
HistoryItemType type() const override { HistoryItemType type() const override {
return HistoryItemCollapse; return HistoryItemCollapse;

View File

@ -0,0 +1,938 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "history/field_autocomplete.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "localstorage.h"
FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent)
, _scroll(this, st::mentionScroll)
, _inner(this, &_mrows, &_hrows, &_brows, &_srows)
, _chat(0)
, _user(0)
, _channel(0)
, _hiding(false)
, a_opacity(0)
, _a_appearance(animation(this, &FieldAutocomplete::step_appearance))
, _shadow(st::dropdownDef.shadow) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(_inner, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(mustScrollTo(int, int)), _scroll, SLOT(scrollToY(int, int)));
connect(App::wnd(), SIGNAL(imageLoaded()), _inner, SLOT(update()));
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()));
}
void FieldAutocomplete::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
p.fillRect(rect(), st::white);
}
void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool addInlineBots) {
_chat = peer->asChat();
_user = peer->asUser();
_channel = peer->asChannel();
if (query.isEmpty()) {
_type = Type::Mentions;
rowsUpdated(internal::MentionRows(), internal::HashtagRows(), internal::BotCommandRows(), _srows, false);
return;
}
_emoji = EmojiPtr();
query = query.toLower();
auto type = Type::Stickers;
auto plainQuery = query.midRef(0);
switch (query.at(0).unicode()) {
case '@':
type = Type::Mentions;
plainQuery = query.midRef(1);
break;
case '#':
type = Type::Hashtags;
plainQuery = query.midRef(1);
break;
case '/':
type = Type::BotCommands;
plainQuery = query.midRef(1);
break;
}
bool resetScroll = (_type != type || _filter != plainQuery);
if (resetScroll) {
_type = type;
_filter = plainQuery.toString();
}
_addInlineBots = addInlineBots;
updateFiltered(resetScroll);
}
void FieldAutocomplete::showStickers(EmojiPtr emoji) {
bool resetScroll = (_emoji != emoji);
_emoji = emoji;
_type = Type::Stickers;
if (!emoji) {
rowsUpdated(_mrows, _hrows, _brows, StickerPack(), false);
return;
}
_chat = 0;
_user = 0;
_channel = 0;
updateFiltered(resetScroll);
}
bool FieldAutocomplete::clearFilteredBotCommands() {
if (_brows.isEmpty()) return false;
_brows.clear();
return true;
}
namespace {
template <typename T, typename U>
inline int indexOfInFirstN(const T &v, const U &elem, int last) {
for (auto b = v.cbegin(), i = b, e = b + qMax(v.size(), last); i != e; ++i) {
if (*i == elem) {
return (i - b);
}
}
return -1;
}
}
void FieldAutocomplete::updateFiltered(bool resetScroll) {
int32 now = unixtime(), recentInlineBots = 0;
internal::MentionRows mrows;
internal::HashtagRows hrows;
internal::BotCommandRows brows;
StickerPack srows;
if (_emoji) {
QMap<uint64, uint64> setsToRequest;
Stickers::Sets &sets(Global::RefStickerSets());
const Stickers::Order &order(Global::StickerSetsOrder());
for (int i = 0, l = order.size(); i < l; ++i) {
auto it = sets.find(order.at(i));
if (it != sets.cend()) {
if (it->emoji.isEmpty()) {
setsToRequest.insert(it->id, it->access);
it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded;
} else if (!(it->flags & MTPDstickerSet::Flag::f_disabled)) {
StickersByEmojiMap::const_iterator i = it->emoji.constFind(emojiGetNoColor(_emoji));
if (i != it->emoji.cend()) {
srows += *i;
}
}
}
}
if (!setsToRequest.isEmpty() && App::api()) {
for (QMap<uint64, uint64>::const_iterator i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
App::api()->scheduleStickerSetRequest(i.key(), i.value());
}
App::api()->requestStickerSets();
}
} else if (_type == Type::Mentions) {
int maxListSize = _addInlineBots ? cRecentInlineBots().size() : 0;
if (_chat) {
maxListSize += (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
} else {
maxListSize += _channel->mgInfo->lastParticipants.size();
}
}
if (maxListSize) {
mrows.reserve(maxListSize);
}
auto filterNotPassedByUsername = [this](UserData *user) -> bool {
if (user->username.startsWith(_filter, Qt::CaseInsensitive)) {
bool exactUsername = (user->username.size() == _filter.size());
return exactUsername;
}
return true;
};
auto filterNotPassedByName = [this](UserData *user) -> bool {
for_const (auto &namePart, user->names) {
if (namePart.startsWith(_filter, Qt::CaseInsensitive)) {
bool exactUsername = (user->username.compare(_filter, Qt::CaseInsensitive) == 0);
return exactUsername;
}
}
return true;
};
bool listAllSuggestions = _filter.isEmpty();
if (_addInlineBots) {
for_const (auto user, cRecentInlineBots()) {
if (!listAllSuggestions && filterNotPassedByUsername(user)) continue;
mrows.push_back(user);
++recentInlineBots;
}
}
if (_chat) {
QMultiMap<int32, UserData*> ordered;
mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
ordered.insertMulti(App::onlineForSort(user, now), user);
}
}
for_const (auto user, _chat->lastAuthors) {
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user, now), user);
}
}
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
mrows.push_back(i.value());
}
}
} else if (_channel && _channel->isMegagroup()) {
QMultiMap<int32, UserData*> ordered;
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_channel);
} else {
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
for_const (auto user, _channel->mgInfo->lastParticipants) {
if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user);
}
}
}
} else if (_type == Type::Hashtags) {
bool listAllSuggestions = _filter.isEmpty();
auto &recent(cRecentWriteHashtags());
hrows.reserve(recent.size());
for (auto i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
if (!listAllSuggestions && (!i->first.startsWith(_filter, Qt::CaseInsensitive) || i->first.size() == _filter.size())) continue;
hrows.push_back(i->first);
}
} else if (_type == Type::BotCommands) {
bool listAllSuggestions = _filter.isEmpty();
bool hasUsername = _filter.indexOf('@') > 0;
QMap<UserData*, bool> bots;
int32 cnt = 0;
if (_chat) {
if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (!user->botInfo) continue;
if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user);
if (user->botInfo->commands.isEmpty()) continue;
bots.insert(user, true);
cnt += user->botInfo->commands.size();
}
}
} else if (_user && _user->botInfo) {
if (!_user->botInfo->inited && App::api()) App::api()->requestFullPeer(_user);
cnt = _user->botInfo->commands.size();
bots.insert(_user, true);
} else if (_channel && _channel->isMegagroup()) {
if (_channel->mgInfo->bots.isEmpty()) {
if (!_channel->mgInfo->botStatus && App::api()) App::api()->requestBots(_channel);
} else {
for_const (auto user, _channel->mgInfo->bots) {
if (!user->botInfo) continue;
if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user);
if (user->botInfo->commands.isEmpty()) continue;
bots.insert(user, true);
cnt += user->botInfo->commands.size();
}
}
}
if (cnt) {
brows.reserve(cnt);
int32 botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : -1);
if (_chat) {
for (auto i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (!user->botInfo) continue;
if (!bots.contains(user)) continue;
if (!user->botInfo->inited && App::api()) App::api()->requestFullPeer(user);
if (user->botInfo->commands.isEmpty()) continue;
bots.remove(user);
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (!listAllSuggestions) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
}
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
}
}
if (!bots.isEmpty()) {
for (QMap<UserData*, bool>::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
UserData *user = i.key();
for (int32 j = 0, l = user->botInfo->commands.size(); j < l; ++j) {
if (!listAllSuggestions) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
}
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
}
}
}
}
rowsUpdated(mrows, hrows, brows, srows, resetScroll);
_inner->setRecentInlineBotsInRows(recentInlineBots);
}
void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const StickerPack &srows, bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) {
if (!isHidden()) {
hideStart();
}
_mrows.clear();
_hrows.clear();
_brows.clear();
_srows.clear();
} else {
_mrows = mrows;
_hrows = hrows;
_brows = brows;
_srows = srows;
bool hidden = _hiding || isHidden();
if (hidden) {
show();
_scroll->show();
}
recount(resetScroll);
update();
if (hidden) {
hide();
showStart();
}
}
}
void FieldAutocomplete::setBoundings(QRect boundings) {
_boundings = boundings;
recount();
}
void FieldAutocomplete::recount(bool resetScroll) {
int32 h = 0, oldst = _scroll->scrollTop(), st = oldst, maxh = 4.5 * st::mentionHeight;
if (!_srows.isEmpty()) {
int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
int32 rows = rowscount(_srows.size(), stickersPerRow);
h = st::stickerPanPadding + rows * st::stickerPanSize.height();
} else if (!_mrows.isEmpty()) {
h = _mrows.size() * st::mentionHeight;
} else if (!_hrows.isEmpty()) {
h = _hrows.size() * st::mentionHeight;
} else if (!_brows.isEmpty()) {
h = _brows.size() * st::mentionHeight;
}
if (_inner->width() != _boundings.width() || _inner->height() != h) {
_inner->resize(_boundings.width(), h);
}
if (h > _boundings.height()) h = _boundings.height();
if (h > maxh) h = maxh;
if (width() != _boundings.width() || height() != h) {
setGeometry(_boundings.x(), _boundings.y() + _boundings.height() - h, _boundings.width(), h);
_scroll->resize(_boundings.width(), h);
} else if (y() != _boundings.y() + _boundings.height() - h) {
move(_boundings.x(), _boundings.y() + _boundings.height() - h);
}
if (resetScroll) st = 0;
if (st != oldst) _scroll->scrollToY(st);
if (resetScroll) _inner->clearSel();
}
void FieldAutocomplete::fastHide() {
if (_a_appearance.animating()) {
_a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
hideFinish();
}
void FieldAutocomplete::hideStart() {
if (!_hiding) {
if (_cache.isNull()) {
_scroll->show();
_cache = myGrab(this);
}
_scroll->hide();
_hiding = true;
a_opacity.start(0);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
}
void FieldAutocomplete::hideFinish() {
hide();
_hiding = false;
_filter = qsl("-");
_inner->clearSel(true);
}
void FieldAutocomplete::showStart() {
if (!isHidden() && a_opacity.current() == 1 && !_hiding) {
return;
}
if (_cache.isNull()) {
_scroll->show();
_cache = myGrab(this);
}
_scroll->hide();
_hiding = false;
show();
a_opacity.start(1);
setAttribute(Qt::WA_OpaquePaintEvent, false);
_a_appearance.start();
}
void FieldAutocomplete::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::dropdownDef.duration;
if (dt >= 1) {
_a_appearance.stop();
a_opacity.finish();
_cache = QPixmap();
setAttribute(Qt::WA_OpaquePaintEvent);
if (_hiding) {
hideFinish();
} else {
_scroll->show();
_inner->clearSel();
}
} else {
a_opacity.update(dt, anim::linear);
}
if (timer) update();
}
const QString &FieldAutocomplete::filter() const {
return _filter;
}
ChatData *FieldAutocomplete::chat() const {
return _chat;
}
ChannelData *FieldAutocomplete::channel() const {
return _channel;
}
UserData *FieldAutocomplete::user() const {
return _user;
}
int32 FieldAutocomplete::innerTop() {
return _scroll->scrollTop();
}
int32 FieldAutocomplete::innerBottom() {
return _scroll->scrollTop() + _scroll->height();
}
bool FieldAutocomplete::chooseSelected(ChooseMethod method) const {
return _inner->chooseSelected(method);
}
bool FieldAutocomplete::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->modifiers() & (Qt::AltModifier | Qt::ControlModifier | Qt::ShiftModifier | Qt::MetaModifier))) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
return _inner->moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner->chooseSelected(ChooseMethod::ByEnter);
}
}
}
return QWidget::eventFilter(obj, e);
}
FieldAutocomplete::~FieldAutocomplete() {
}
namespace internal {
FieldAutocompleteInner::FieldAutocompleteInner(FieldAutocomplete *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows)
: _parent(parent)
, _mrows(mrows)
, _hrows(hrows)
, _brows(brows)
, _srows(srows)
, _stickersPerRow(1)
, _recentInlineBotsInRows(0)
, _sel(-1)
, _down(-1)
, _mouseSel(false)
, _overDelete(false)
, _previewShown(false) {
_previewTimer.setSingleShot(true);
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview()));
}
void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect());
if (r != rect()) p.setClipRect(r);
int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#');
int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize;
int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right();
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
for (int32 row = fromrow; row < torow; ++row) {
for (int32 col = fromcol; col < tocol; ++col) {
int32 index = row * _stickersPerRow + col;
if (index >= _srows->size()) break;
DocumentData *sticker = _srows->at(index);
if (!sticker->sticker()) continue;
QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height());
if (_sel == index) {
QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width());
App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
}
bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128));
if (goodThumb) {
sticker->thumb->load();
} else {
sticker->checkSticker();
}
float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height()));
if (coef > 1) coef = 1;
int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height());
if (w < 1) w = 1;
if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
if (goodThumb) {
p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h));
} else if (!sticker->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h));
}
}
}
} else {
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
auto filter = _parent->filter();
bool hasUsername = filter.indexOf('@') > 0;
int filterSize = filter.size();
bool filterIsEmpty = filter.isEmpty();
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
bool selected = (i == _sel);
if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) {
p.drawSprite(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), st::notifyClose.icon);
}
}
p.setPen(st::black->p);
if (!_mrows->isEmpty()) {
UserData *user = _mrows->at(i);
QString first = (!filterIsEmpty && user->username.startsWith(filter, Qt::CaseInsensitive)) ? ('@' + user->username.mid(0, filterSize)) : QString();
QString second = first.isEmpty() ? (user->username.isEmpty() ? QString() : ('@' + user->username)) : user->username.mid(filterSize);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (mentionwidth < unamewidth + namewidth) {
namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
unamewidth = mentionwidth - namewidth;
if (firstwidth < unamewidth + st::mentionFont->elidew) {
if (firstwidth < unamewidth) {
first = st::mentionFont->elided(first, unamewidth);
} else if (!second.isEmpty()) {
first = st::mentionFont->elided(first + second, unamewidth);
second = QString();
}
} else {
second = st::mentionFont->elided(second, unamewidth - firstwidth);
}
}
user->loadUserpic();
user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width());
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
p.setFont(st::mentionFont->f);
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else if (!_hrows->isEmpty()) {
QString hrow = _hrows->at(i);
QString first = filterIsEmpty ? QString() : ('#' + hrow.mid(0, filterSize));
QString second = filterIsEmpty ? ('#' + hrow) : hrow.mid(filterSize);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (htagwidth < firstwidth + secondwidth) {
if (htagwidth < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, htagwidth);
second = QString();
} else {
second = st::mentionFont->elided(second, htagwidth - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else {
UserData *user = _brows->at(i).first;
const BotCommand *command = _brows->at(i).second;
QString toHighlight = command->command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) {
toHighlight += '@' + user->username;
}
user->loadUserpic();
user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width());
int32 addleft = 0, widthleft = mentionwidth;
QString first = filterIsEmpty ? QString() : ('/' + toHighlight.mid(0, filterSize));
QString second = filterIsEmpty ? ('/' + toHighlight) : toHighlight.mid(filterSize);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (widthleft < firstwidth + secondwidth) {
if (widthleft < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, widthleft);
second = QString();
} else {
second = st::mentionFont->elided(second, widthleft - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
addleft += firstwidth + secondwidth + st::mentionPadding.left();
widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft, 1, style::al_right);
}
}
}
p.fillRect(Adaptive::OneColumn() ? 0 : st::lineWidth, _parent->innerBottom() - st::lineWidth, width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::lineWidth, st::shadowColor->b);
}
p.fillRect(Adaptive::OneColumn() ? 0 : st::lineWidth, _parent->innerTop(), width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::lineWidth, st::shadowColor->b);
}
void FieldAutocompleteInner::resizeEvent(QResizeEvent *e) {
_stickersPerRow = qMax(1, int32(width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
}
void FieldAutocompleteInner::mouseMoveEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
}
void FieldAutocompleteInner::clearSel(bool hidden) {
_mouseSel = _overDelete = false;
setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0);
if (hidden) {
_down = -1;
_previewShown = false;
}
}
bool FieldAutocompleteInner::moveSel(int key) {
_mouseSel = false;
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size());
int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0);
if (!_srows->isEmpty()) {
if (key == Qt::Key_Left) {
direction = -1;
} else if (key == Qt::Key_Right) {
direction = 1;
} else {
direction *= _stickersPerRow;
}
}
if (_sel >= maxSel || _sel < 0) {
if (direction < -1) {
setSel(((maxSel - 1) / _stickersPerRow) * _stickersPerRow, true);
} else if (direction < 0) {
setSel(maxSel - 1, true);
} else {
setSel(0, true);
}
return (_sel >= 0 && _sel < maxSel);
}
setSel((_sel + direction >= maxSel || _sel + direction < 0) ? -1 : (_sel + direction), true);
return true;
}
bool FieldAutocompleteInner::chooseSelected(FieldAutocomplete::ChooseMethod method) const {
if (!_srows->isEmpty()) {
if (_sel >= 0 && _sel < _srows->size()) {
emit stickerChosen(_srows->at(_sel), method);
return true;
}
} else if (!_mrows->isEmpty()) {
if (_sel >= 0 && _sel < _mrows->size()) {
emit mentionChosen(_mrows->at(_sel), method);
return true;
}
} else if (!_hrows->isEmpty()) {
if (_sel >= 0 && _sel < _hrows->size()) {
emit hashtagChosen('#' + _hrows->at(_sel), method);
return true;
}
} else if (!_brows->isEmpty()) {
if (_sel >= 0 && _sel < _brows->size()) {
UserData *user = _brows->at(_sel).first;
const BotCommand *command(_brows->at(_sel).second);
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 0) {
emit botCommandChosen('/' + command->command + '@' + user->username, method);
} else {
emit botCommandChosen('/' + command->command, method);
}
return true;
}
}
return false;
}
void FieldAutocompleteInner::setRecentInlineBotsInRows(int32 bots) {
_recentInlineBotsInRows = bots;
}
void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) {
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
if (e->button() == Qt::LeftButton) {
if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) {
_mousePos = mapToGlobal(e->pos());
bool removed = false;
if (_mrows->isEmpty()) {
QString toRemove = _hrows->at(_sel);
RecentHashtagPack &recent(cRefRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
if (i->first == toRemove) {
i = recent.erase(i);
removed = true;
} else {
++i;
}
}
} else {
UserData *toRemove = _mrows->at(_sel);
RecentInlineBots &recent(cRefRecentInlineBots());
int32 index = recent.indexOf(toRemove);
if (index >= 0) {
recent.remove(index);
removed = true;
}
}
if (removed) {
Local::writeRecentHashtagsAndBots();
}
_parent->updateFiltered();
_mouseSel = true;
onUpdateSelected(true);
} else if (_srows->isEmpty()) {
chooseSelected(FieldAutocomplete::ChooseMethod::ByClick);
} else {
_down = _sel;
_previewTimer.start(QApplication::startDragTime());
}
}
}
void FieldAutocompleteInner::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.stop();
int32 pressed = _down;
_down = -1;
_mousePos = mapToGlobal(e->pos());
_mouseSel = true;
onUpdateSelected(true);
if (_previewShown) {
_previewShown = false;
return;
}
if (_sel < 0 || _sel != pressed || _srows->isEmpty()) return;
chooseSelected(FieldAutocomplete::ChooseMethod::ByClick);
}
void FieldAutocompleteInner::enterEvent(QEvent *e) {
setMouseTracking(true);
_mousePos = QCursor::pos();
onUpdateSelected(true);
}
void FieldAutocompleteInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
if (_sel >= 0) {
setSel(-1);
}
}
void FieldAutocompleteInner::updateSelectedRow() {
if (_sel >= 0) {
if (_srows->isEmpty()) {
update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow, col = _sel % _stickersPerRow;
update(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanSize.width(), st::stickerPanSize.height());
}
}
}
void FieldAutocompleteInner::setSel(int sel, bool scroll) {
updateSelectedRow();
_sel = sel;
updateSelectedRow();
if (scroll && _sel >= 0) {
if (_srows->isEmpty()) {
emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow;
emit mustScrollTo(st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanPadding + (row + 1) * st::stickerPanSize.height());
}
}
}
void FieldAutocompleteInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(_mousePos));
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
if (_down >= 0 && !_previewShown) return;
int32 sel = -1, maxSel = 0;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 row = (mouse.y() >= st::stickerPanPadding) ? ((mouse.y() - st::stickerPanPadding) / st::stickerPanSize.height()) : -1;
int32 col = (mouse.x() >= st::stickerPanPadding) ? ((mouse.x() - st::stickerPanPadding) / st::stickerPanSize.width()) : -1;
if (row >= 0 && col >= 0) {
sel = row * _stickersPerRow + col;
}
maxSel = _srows->size();
_overDelete = false;
} else {
sel = mouse.y() / int32(st::mentionHeight);
maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false;
}
if (sel < 0 || sel >= maxSel) {
sel = -1;
}
if (sel != _sel) {
setSel(sel);
if (_down >= 0 && _sel >= 0 && _down != _sel) {
_down = _sel;
if (_down >= 0 && _down < _srows->size()) {
Ui::showMediaPreview(_srows->at(_down));
}
}
}
}
void FieldAutocompleteInner::onParentGeometryChanged() {
_mousePos = QCursor::pos();
if (rect().contains(mapFromGlobal(_mousePos))) {
setMouseTracking(true);
onUpdateSelected(true);
}
}
void FieldAutocompleteInner::onPreview() {
if (_down >= 0 && _down < _srows->size()) {
Ui::showMediaPreview(_srows->at(_down));
_previewShown = true;
}
}
} // namespace internal

View File

@ -0,0 +1,202 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/twidget.h"
#include "ui/boxshadow.h"
namespace internal {
using MentionRows = QList<UserData*>;
using HashtagRows = QList<QString>;
using BotCommandRows = QList<QPair<UserData*, const BotCommand*>>;
class FieldAutocompleteInner;
} // namespace internal
class FieldAutocomplete final : public TWidget {
Q_OBJECT
public:
FieldAutocomplete(QWidget *parent);
void fastHide();
bool clearFilteredBotCommands();
void showFiltered(PeerData *peer, QString query, bool addInlineBots);
void showStickers(EmojiPtr emoji);
void setBoundings(QRect boundings);
void step_appearance(float64 ms, bool timer);
const QString &filter() const;
ChatData *chat() const;
ChannelData *channel() const;
UserData *user() const;
int32 innerTop();
int32 innerBottom();
bool eventFilter(QObject *obj, QEvent *e) override;
enum class ChooseMethod {
ByEnter,
ByTab,
ByClick,
};
bool chooseSelected(ChooseMethod method) const;
bool stickersShown() const {
return !_srows.isEmpty();
}
bool overlaps(const QRect &globalRect) {
if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false;
return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
~FieldAutocomplete();
signals:
void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const;
void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const;
void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const;
void stickerChosen(DocumentData *sticker, FieldAutocomplete::ChooseMethod method) const;
public slots:
void hideStart();
void hideFinish();
void showStart();
private:
void paintEvent(QPaintEvent *e) override;
void updateFiltered(bool resetScroll = false);
void recount(bool resetScroll = false);
QPixmap _cache;
internal::MentionRows _mrows;
internal::HashtagRows _hrows;
internal::BotCommandRows _brows;
StickerPack _srows;
void rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const StickerPack &srows, bool resetScroll);
ChildWidget<ScrollArea> _scroll;
ChildWidget<internal::FieldAutocompleteInner> _inner;
ChatData *_chat;
UserData *_user;
ChannelData *_channel;
EmojiPtr _emoji;
enum class Type {
Mentions,
Hashtags,
BotCommands,
Stickers,
};
Type _type = Type::Mentions;
QString _filter;
QRect _boundings;
bool _addInlineBots;
int32 _width, _height;
bool _hiding;
anim::fvalue a_opacity;
Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
friend class internal::FieldAutocompleteInner;
};
namespace internal {
class FieldAutocompleteInner final : public TWidget {
Q_OBJECT
public:
FieldAutocompleteInner(FieldAutocomplete *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows);
void clearSel(bool hidden = false);
bool moveSel(int key);
bool chooseSelected(FieldAutocomplete::ChooseMethod method) const;
void setRecentInlineBotsInRows(int32 bots);
signals:
void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const;
void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const;
void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const;
void stickerChosen(DocumentData *sticker, FieldAutocomplete::ChooseMethod method) const;
void mustScrollTo(int scrollToTop, int scrollToBottom);
public slots:
void onParentGeometryChanged();
void onUpdateSelected(bool force = false);
void onPreview();
private:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void updateSelectedRow();
void setSel(int sel, bool scroll = false);
FieldAutocomplete *_parent;
MentionRows *_mrows;
HashtagRows *_hrows;
BotCommandRows *_brows;
StickerPack *_srows;
int32 _stickersPerRow, _recentInlineBotsInRows;
int32 _sel, _down;
bool _mouseSel;
QPoint _mousePos;
bool _overDelete;
bool _previewShown;
QTimer _previewTimer;
};
} // namespace internal

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/boxshadow.h" #include "ui/boxshadow.h"
#include "dropdown.h" #include "dropdown.h"
#include "history/history_common.h" #include "history/history_common.h"
#include "history/field_autocomplete.h"
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
class HistoryWidget; class HistoryWidget;
class HistoryInner : public TWidget, public AbstractTooltipShower { class HistoryInner : public TWidget, public AbstractTooltipShower {
@ -49,7 +57,7 @@ public:
void keyPressEvent(QKeyEvent *e) override; void keyPressEvent(QKeyEvent *e) override;
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
QString getSelectedText() const; TextWithEntities getSelectedText() const;
void dragActionStart(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton); void dragActionStart(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton);
void dragActionUpdate(const QPoint &screenPos); void dragActionUpdate(const QPoint &screenPos);
@ -136,6 +144,8 @@ private:
HistoryItem *nextItem(HistoryItem *item); HistoryItem *nextItem(HistoryItem *item);
void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false); void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false);
void setToClipboard(const TextWithEntities &forClipboard);
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
History *_migrated = nullptr; History *_migrated = nullptr;
History *_history = nullptr; History *_history = nullptr;
@ -478,17 +488,8 @@ public:
}; };
enum TextUpdateEventsFlags { EntitiesInText entitiesFromTextTags(const TextWithTags::Tags &tags);
TextUpdateEventsSaveDraft = 0x01, TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities);
TextUpdateEventsSendTyping = 0x02,
};
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
class HistoryWidget : public TWidget, public RPCSender { class HistoryWidget : public TWidget, public RPCSender {
Q_OBJECT Q_OBJECT
@ -547,7 +548,6 @@ public:
void destroyData(); void destroyData();
void updateFieldPlaceholder(); void updateFieldPlaceholder();
void updateInlineBotQuery();
void updateStickersByEmoji(); void updateStickersByEmoji();
void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false); void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false);
@ -759,7 +759,6 @@ public slots:
void activate(); void activate();
void onStickersUpdated(); void onStickersUpdated();
void onMentionHashtagOrBotCommandInsert(QString str);
void onTextChange(); void onTextChange();
void onFieldTabbed(); void onFieldTabbed();
@ -777,7 +776,7 @@ public slots:
void onFieldFocused(); void onFieldFocused();
void onFieldResize(); void onFieldResize();
void onCheckMentionDropdown(); void onCheckFieldAutocomplete();
void onScrollTimer(); void onScrollTimer();
void onForwardSelected(); void onForwardSelected();
@ -792,7 +791,6 @@ public slots:
void onDraftSave(bool delayed = false); void onDraftSave(bool delayed = false);
void updateStickers(); void updateStickers();
void updateField();
void onRecordError(); void onRecordError();
void onRecordDone(QByteArray result, VoiceWaveform waveform, qint32 samples); void onRecordDone(QByteArray result, VoiceWaveform waveform, qint32 samples);
@ -806,8 +804,12 @@ public slots:
private slots: private slots:
void onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method);
void onMentionInsert(UserData *user);
void onInlineBotCancel(); void onInlineBotCancel();
void updateField();
private: private:
// Updates position of controls around the message field, // Updates position of controls around the message field,
@ -818,6 +820,12 @@ private:
void clearInlineBot(); void clearInlineBot();
void inlineBotChanged(); void inlineBotChanged();
// Look in the _field for the inline bot and query string.
void updateInlineBotQuery();
// Request to show results in the emoji panel.
void applyInlineBotQuery(UserData *bot, const QString &query);
MsgId _replyToId = 0; MsgId _replyToId = 0;
Text _replyToName; Text _replyToName;
int _replyToNameVersion = 0; int _replyToNameVersion = 0;
@ -827,6 +835,7 @@ private:
HistoryItem *_replyEditMsg = nullptr; HistoryItem *_replyEditMsg = nullptr;
Text _replyEditMsgText; Text _replyEditMsgText;
mutable SingleTimer _updateEditTimeLeftDisplay;
IconedButton _fieldBarCancel; IconedButton _fieldBarCancel;
void updateReplyEditTexts(bool force = false); void updateReplyEditTexts(bool force = false);
@ -854,7 +863,8 @@ private:
void sendExistingDocument(DocumentData *doc, const QString &caption); void sendExistingDocument(DocumentData *doc, const QString &caption);
void sendExistingPhoto(PhotoData *photo, const QString &caption); void sendExistingPhoto(PhotoData *photo, const QString &caption);
void drawField(Painter &p); void drawField(Painter &p, const QRect &rect);
void paintEditHeader(Painter &p, const QRect &rect, int left, int top) const;
void drawRecordButton(Painter &p); void drawRecordButton(Painter &p);
void drawRecording(Painter &p); void drawRecording(Painter &p);
void drawPinnedBar(Painter &p); void drawPinnedBar(Painter &p);
@ -950,11 +960,18 @@ private:
void savedGifsGot(const MTPmessages_SavedGifs &gifs); void savedGifsGot(const MTPmessages_SavedGifs &gifs);
bool savedGifsFailed(const RPCError &error); bool savedGifsFailed(const RPCError &error);
enum class TextUpdateEvent {
SaveDraft = 0x01,
SendTyping = 0x02,
};
Q_DECLARE_FLAGS(TextUpdateEvents, TextUpdateEvent);
Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(TextUpdateEvents);
void writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft); void writeDrafts(HistoryDraft **msgDraft, HistoryEditDraft **editDraft);
void writeDrafts(History *history); void writeDrafts(History *history);
void setFieldText(const QString &text, int32 textUpdateEventsFlags = 0, bool clearUndoHistory = true); void setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events = 0, FlatTextarea::UndoHistoryAction undoHistoryAction = FlatTextarea::ClearUndoHistory);
void clearFieldText(int32 textUpdateEventsFlags = 0, bool clearUndoHistory = true) { void clearFieldText(TextUpdateEvents events = 0, FlatTextarea::UndoHistoryAction undoHistoryAction = FlatTextarea::ClearUndoHistory) {
setFieldText(QString()); setFieldText(TextWithTags(), events, undoHistoryAction);
} }
QStringList getMediasFromMime(const QMimeData *d); QStringList getMediasFromMime(const QMimeData *d);
@ -1003,7 +1020,7 @@ private:
IconedButton _toHistoryEnd; IconedButton _toHistoryEnd;
CollapseButton _collapseComments; CollapseButton _collapseComments;
MentionsDropdown _attachMention; ChildWidget<FieldAutocomplete> _fieldAutocomplete;
UserData *_inlineBot = nullptr; UserData *_inlineBot = nullptr;
QString _inlineBotUsername; QString _inlineBotUsername;
@ -1058,7 +1075,7 @@ private:
int32 _selCount; // < 0 - text selected, focus list, not _field int32 _selCount; // < 0 - text selected, focus list, not _field
TaskQueue _fileLoader; TaskQueue _fileLoader;
int32 _textUpdateEventsFlags = (TextUpdateEventsSaveDraft | TextUpdateEventsSendTyping); TextUpdateEvents _textUpdateEvents = (TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping);
int64 _serviceImageCacheSize = 0; int64 _serviceImageCacheSize = 0;
QString _confirmSource; QString _confirmSource;
@ -1091,3 +1108,4 @@ private:
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(HistoryWidget::TextUpdateEvents)

View File

@ -19,9 +19,10 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
#include "stdafx.h" #include "stdafx.h"
#include "lang.h" #include "lang.h"
#include "langloaderplain.h"
LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang dependent LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang dependent
int v = qFloor(value); int v = qFloor(value);
QString sv; QString sv;
@ -45,6 +46,43 @@ LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang
return lang(LangKey(key0)).tag(tag, sv); return lang(LangKey(key0)).tag(tag, sv);
} }
#define NEW_VER_TAG lt_link
#define NEW_VER_TAG_VALUE "https://telegram.org/blog/edit"
QString langNewVersionText() {
#ifdef NEW_VER_TAG
return lng_new_version_text(NEW_VER_TAG, QString::fromUtf8(NEW_VER_TAG_VALUE));
#else // NEW_VER_TAG
return lang(lng_new_version_text);
#endif // NEW_VER_TAG
}
#ifdef NEW_VER_TAG
#define NEW_VER_KEY lng_new_version_text__tagged
#define NEW_VER_POSTFIX .tag(NEW_VER_TAG, QString::fromUtf8(NEW_VER_TAG_VALUE))
#else // NEW_VER_TAG
#define NEW_VER_KEY lng_new_version_text
#define NEW_VER_POSTFIX
#endif // NEW_VER_TAG
QString langNewVersionTextForLang(int langId) {
LangLoaderResult result;
if (langId) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name, NEW_VER_KEY));
result = loader.found();
} else {
result.insert(lng_language_name, langOriginal(lng_language_name));
result.insert(NEW_VER_KEY, langOriginal(NEW_VER_KEY));
}
return result.value(lng_language_name, LanguageCodes[langId].c_str() + qsl(" language")) + qsl(":\n\n") + LangString(result.value(NEW_VER_KEY, qsl("--none--")))NEW_VER_POSTFIX;
}
#undef NEW_VER_POSTFIX
#undef NEW_VER_KEY
#undef NEW_VER_TAG_VALUE
#undef NEW_VER_TAG
const QString &LangLoader::errors() const { const QString &LangLoader::errors() const {
if (_errors.isEmpty() && !_err.isEmpty()) { if (_errors.isEmpty() && !_err.isEmpty()) {
_errors = _err.join('\n'); _errors = _err.join('\n');

View File

@ -142,9 +142,8 @@ inline LangString langDateTimeFull(const QDateTime &date) {
return lng_mediaview_date_time(lt_date, langDayOfMonthFull(date.date()), lt_time, date.time().toString(cTimeFormat())); return lng_mediaview_date_time(lt_date, langDayOfMonthFull(date.date()), lt_time, date.time().toString(cTimeFormat()));
} }
inline LangString langNewVersionText() { QString langNewVersionText();
return lng_new_version_text(lt_link, qsl("https://telegram.org/blog/bots-2-0")); QString langNewVersionTextForLang(int langId);
}
class LangLoader { class LangLoader {
public: public:

View File

@ -122,14 +122,6 @@ namespace {
return true; return true;
} }
uint32 _dateTimeSize() {
return (sizeof(qint64) + sizeof(quint32) + sizeof(qint8));
}
uint32 _bytearraySize(const QByteArray &arr) {
return sizeof(quint32) + arr.size();
}
QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted; QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted;
MTP::AuthKey _oldKey, _settingsKey, _passKey, _localKey; MTP::AuthKey _oldKey, _settingsKey, _passKey, _localKey;
@ -628,18 +620,18 @@ namespace {
size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(i.value().name()); size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(i.value().name());
if (AppVersion > 9013) { if (AppVersion > 9013) {
// bookmark // bookmark
size += _bytearraySize(i.value().bookmark()); size += Serialize::bytearraySize(i.value().bookmark());
} }
// date + size // date + size
size += _dateTimeSize() + sizeof(quint32); size += Serialize::dateTimeSize() + sizeof(quint32);
} }
//end mark //end mark
size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(QString()); size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(QString());
if (AppVersion > 9013) { if (AppVersion > 9013) {
size += _bytearraySize(QByteArray()); size += Serialize::bytearraySize(QByteArray());
} }
size += _dateTimeSize() + sizeof(quint32); size += Serialize::dateTimeSize() + sizeof(quint32);
size += sizeof(quint32); // aliases count size += sizeof(quint32); // aliases count
for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) {
@ -1530,7 +1522,7 @@ namespace {
} }
uint32 size = 16 * (sizeof(quint32) + sizeof(qint32)); uint32 size = 16 * (sizeof(quint32) + sizeof(qint32));
size += sizeof(quint32) + Serialize::stringSize(cAskDownloadPath() ? QString() : cDownloadPath()) + _bytearraySize(cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark()); size += sizeof(quint32) + Serialize::stringSize(cAskDownloadPath() ? QString() : cDownloadPath()) + Serialize::bytearraySize(cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark());
size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort));
size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64)); size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64));
size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort));
@ -2281,8 +2273,8 @@ namespace Local {
void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft) { void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft) {
if (!_working()) return; if (!_working()) return;
if (msgDraft.msgId <= 0 && msgDraft.text.isEmpty() && editDraft.msgId <= 0) { if (msgDraft.msgId <= 0 && msgDraft.textWithTags.text.isEmpty() && editDraft.msgId <= 0) {
DraftsMap::iterator i = _draftsMap.find(peer); auto i = _draftsMap.find(peer);
if (i != _draftsMap.cend()) { if (i != _draftsMap.cend()) {
clearKey(i.value()); clearKey(i.value());
_draftsMap.erase(i); _draftsMap.erase(i);
@ -2292,18 +2284,25 @@ namespace Local {
_draftsNotReadMap.remove(peer); _draftsNotReadMap.remove(peer);
} else { } else {
DraftsMap::const_iterator i = _draftsMap.constFind(peer); auto i = _draftsMap.constFind(peer);
if (i == _draftsMap.cend()) { if (i == _draftsMap.cend()) {
i = _draftsMap.insert(peer, genKey()); i = _draftsMap.insert(peer, genKey());
_mapChanged = true; _mapChanged = true;
_writeMap(WriteMapFast); _writeMap(WriteMapFast);
} }
EncryptedDescriptor data(sizeof(quint64) + Serialize::stringSize(msgDraft.text) + 2 * sizeof(qint32) + Serialize::stringSize(editDraft.text) + 2 * sizeof(qint32)); auto msgTags = FlatTextarea::serializeTagsList(msgDraft.textWithTags.tags);
auto editTags = FlatTextarea::serializeTagsList(editDraft.textWithTags.tags);
int size = sizeof(quint64);
size += Serialize::stringSize(msgDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32);
size += Serialize::stringSize(editDraft.textWithTags.text) + Serialize::bytearraySize(editTags) + 2 * sizeof(qint32);
EncryptedDescriptor data(size);
data.stream << quint64(peer); data.stream << quint64(peer);
data.stream << msgDraft.text << QByteArray(); data.stream << msgDraft.textWithTags.text << msgTags;
data.stream << qint32(msgDraft.msgId) << qint32(msgDraft.previewCancelled ? 1 : 0); data.stream << qint32(msgDraft.msgId) << qint32(msgDraft.previewCancelled ? 1 : 0);
data.stream << editDraft.text << QByteArray(); data.stream << editDraft.textWithTags.text << editTags;
data.stream << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0); data.stream << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0);
FileWriteDescriptor file(i.value()); FileWriteDescriptor file(i.value());
@ -2372,10 +2371,10 @@ namespace Local {
} }
quint64 draftPeer = 0; quint64 draftPeer = 0;
QString msgText, editText; TextWithTags msgData, editData;
QByteArray msgTagsSerialized, editTagsSerialized; QByteArray msgTagsSerialized, editTagsSerialized;
qint32 msgReplyTo = 0, msgPreviewCancelled = 0, editMsgId = 0, editPreviewCancelled = 0; qint32 msgReplyTo = 0, msgPreviewCancelled = 0, editMsgId = 0, editPreviewCancelled = 0;
draft.stream >> draftPeer >> msgText; draft.stream >> draftPeer >> msgData.text;
if (draft.version >= 9048) { if (draft.version >= 9048) {
draft.stream >> msgTagsSerialized; draft.stream >> msgTagsSerialized;
} }
@ -2384,7 +2383,7 @@ namespace Local {
if (draft.version >= 8001) { if (draft.version >= 8001) {
draft.stream >> msgPreviewCancelled; draft.stream >> msgPreviewCancelled;
if (!draft.stream.atEnd()) { if (!draft.stream.atEnd()) {
draft.stream >> editText; draft.stream >> editData.text;
if (draft.version >= 9048) { if (draft.version >= 9048) {
draft.stream >> editTagsSerialized; draft.stream >> editTagsSerialized;
} }
@ -2399,18 +2398,21 @@ namespace Local {
return; return;
} }
msgData.tags = FlatTextarea::deserializeTagsList(msgTagsSerialized, msgData.text.size());
editData.tags = FlatTextarea::deserializeTagsList(editTagsSerialized, editData.text.size());
MessageCursor msgCursor, editCursor; MessageCursor msgCursor, editCursor;
_readDraftCursors(peer, msgCursor, editCursor); _readDraftCursors(peer, msgCursor, editCursor);
if (msgText.isEmpty() && !msgReplyTo) { if (msgData.text.isEmpty() && !msgReplyTo) {
h->clearMsgDraft(); h->clearMsgDraft();
} else { } else {
h->setMsgDraft(std_::make_unique<HistoryDraft>(msgText, msgReplyTo, msgCursor, msgPreviewCancelled)); h->setMsgDraft(std_::make_unique<HistoryDraft>(msgData, msgReplyTo, msgCursor, msgPreviewCancelled));
} }
if (!editMsgId) { if (!editMsgId) {
h->clearEditDraft(); h->clearEditDraft();
} else { } else {
h->setEditDraft(std_::make_unique<HistoryEditDraft>(editText, editMsgId, editCursor, editPreviewCancelled)); h->setEditDraft(std_::make_unique<HistoryEditDraft>(editData, editMsgId, editCursor, editPreviewCancelled));
} }
} }
@ -3029,7 +3031,7 @@ namespace Local {
} else { } else {
int32 setsCount = 0; int32 setsCount = 0;
QByteArray hashToWrite; QByteArray hashToWrite;
quint32 size = sizeof(quint32) + _bytearraySize(hashToWrite); quint32 size = sizeof(quint32) + Serialize::bytearraySize(hashToWrite);
for (auto i = sets.cbegin(); i != sets.cend(); ++i) { for (auto i = sets.cbegin(); i != sets.cend(); ++i) {
bool notLoaded = (i->flags & MTPDstickerSet_ClientFlag::f_not_loaded); bool notLoaded = (i->flags & MTPDstickerSet_ClientFlag::f_not_loaded);
if (notLoaded) { if (notLoaded) {
@ -3692,7 +3694,7 @@ namespace Local {
} }
quint32 size = sizeof(quint32); quint32 size = sizeof(quint32);
for (SavedPeers::const_iterator i = saved.cbegin(); i != saved.cend(); ++i) { for (SavedPeers::const_iterator i = saved.cbegin(); i != saved.cend(); ++i) {
size += _peerSize(i.key()) + _dateTimeSize(); size += _peerSize(i.key()) + Serialize::dateTimeSize();
} }
EncryptedDescriptor data(size); EncryptedDescriptor data(size);

View File

@ -105,11 +105,15 @@ namespace Local {
int32 oldSettingsVersion(); int32 oldSettingsVersion();
using TextWithTags = FlatTextarea::TextWithTags;
struct MessageDraft { struct MessageDraft {
MessageDraft(MsgId msgId = 0, QString text = QString(), bool previewCancelled = false) : msgId(msgId), text(text), previewCancelled(previewCancelled) { MessageDraft(MsgId msgId = 0, TextWithTags textWithTags = TextWithTags(), bool previewCancelled = false)
: msgId(msgId)
, textWithTags(textWithTags)
, previewCancelled(previewCancelled) {
} }
MsgId msgId; MsgId msgId;
QString text; TextWithTags textWithTags;
bool previewCancelled; bool previewCancelled;
}; };
void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft); void writeDrafts(const PeerId &peer, const MessageDraft &msgDraft, const MessageDraft &editDraft);

View File

@ -25,6 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h" #include "localstorage.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
#ifndef Q_OS_MAC // Retina display support is working fine, others are not.
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
#endif // Q_OS_MAC
settingsParseArgs(argc, argv); settingsParseArgs(argc, argv);
if (cLaunchMode() == LaunchModeFixPrevious) { if (cLaunchMode() == LaunchModeFixPrevious) {
return psFixPrevious(); return psFixPrevious();

View File

@ -43,7 +43,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h" #include "localstorage.h"
#include "shortcuts.h" #include "shortcuts.h"
#include "audio.h" #include "audio.h"
#include "langloaderplain.h"
MainWidget::MainWidget(MainWindow *window) : TWidget(window) MainWidget::MainWidget(MainWindow *window) : TWidget(window)
, _a_show(animation(this, &MainWidget::step_show)) , _a_show(animation(this, &MainWidget::step_show))
@ -159,7 +158,9 @@ bool MainWidget::onShareUrl(const PeerId &peer, const QString &url, const QStrin
return false; return false;
} }
History *h = App::history(peer); History *h = App::history(peer);
h->setMsgDraft(std_::make_unique<HistoryDraft>(url + '\n' + text, 0, MessageCursor(url.size() + 1, url.size() + 1 + text.size(), QFIXED_MAX), false)); TextWithTags textWithTags = { url + '\n' + text, TextWithTags::Tags() };
MessageCursor cursor = { url.size() + 1, url.size() + 1 + text.size(), QFIXED_MAX };
h->setMsgDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->clearEditDraft(); h->clearEditDraft();
bool opened = _history->peer() && (_history->peer()->id == peer); bool opened = _history->peer() && (_history->peer()->id == peer);
if (opened) { if (opened) {
@ -177,7 +178,9 @@ bool MainWidget::onInlineSwitchChosen(const PeerId &peer, const QString &botAndQ
return false; return false;
} }
History *h = App::history(peer); History *h = App::history(peer);
h->setMsgDraft(std_::make_unique<HistoryDraft>(botAndQuery, 0, MessageCursor(botAndQuery.size(), botAndQuery.size(), QFIXED_MAX), false)); TextWithTags textWithTags = { botAndQuery, TextWithTags::Tags() };
MessageCursor cursor = { botAndQuery.size(), botAndQuery.size(), QFIXED_MAX };
h->setMsgDraft(std_::make_unique<HistoryDraft>(textWithTags, 0, cursor, false));
h->clearEditDraft(); h->clearEditDraft();
bool opened = _history->peer() && (_history->peer()->id == peer); bool opened = _history->peer() && (_history->peer()->id == peer);
if (opened) { if (opened) {
@ -1051,83 +1054,61 @@ void executeParsedCommand(const QString &command) {
if (command == qsl("new_version_text")) { if (command == qsl("new_version_text")) {
App::wnd()->serviceNotification(langNewVersionText()); App::wnd()->serviceNotification(langNewVersionText());
} else if (command == qsl("all_new_version_texts")) { } else if (command == qsl("all_new_version_texts")) {
#define NEW_VER_TAG lt_link
#define NEW_VER_TAG_VALUE "https://telegram.org/blog/bots-2-0"
#ifdef NEW_VER_TAG
#define NEW_VER_KEY lng_new_version_text__tagged
#define NEW_VER_POSTFIX .tag(NEW_VER_TAG, QString::fromUtf8(NEW_VER_TAG_VALUE))
#else
#define NEW_VER_KEY lng_new_version_text
#define NEW_VER_POSTFIX
#endif
for (int i = 0; i < languageCount; ++i) { for (int i = 0; i < languageCount; ++i) {
LangLoaderResult result; App::wnd()->serviceNotification(langNewVersionTextForLang(i));
if (i) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name, NEW_VER_KEY));
result = loader.found();
} else {
result.insert(lng_language_name, langOriginal(lng_language_name));
result.insert(NEW_VER_KEY, langOriginal(NEW_VER_KEY));
} }
App::wnd()->serviceNotification(result.value(lng_language_name, LanguageCodes[i].c_str() + qsl(" language")) + qsl(":\n\n") + LangString(result.value(NEW_VER_KEY, qsl("--none--")))NEW_VER_POSTFIX);
}
#undef NEW_VER_POSTFIX
#undef NEW_VER_KEY
#undef NEW_VER_TAG_VALUE
#undef NEW_VER_TAG
} }
} }
} // namespace } // namespace
void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast, bool silent, WebPageId webPageId) { void MainWidget::sendMessage(const MessageToSend &message) {
readServerHistory(hist, false); auto history = message.history;
_history->fastShowAtEnd(hist); const auto &textWithTags = message.textWithTags;
if (!hist || !_history->canSendMessages(hist->peer)) { readServerHistory(history, false);
_history->fastShowAtEnd(history);
if (!history || !_history->canSendMessages(history->peer)) {
return; return;
} }
saveRecentHashtags(text); saveRecentHashtags(textWithTags.text);
EntitiesInText sendingEntities, leftEntities; EntitiesInText sendingEntities, leftEntities = entitiesFromTextTags(textWithTags.tags);
QString sendingText, leftText = prepareTextWithEntities(text, leftEntities, itemTextOptions(hist, App::self()).flags); auto prepareFlags = itemTextOptions(history, App::self()).flags;
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities);
QString command = parseCommandFromMessage(hist, text); QString command = parseCommandFromMessage(history, textWithTags.text);
HistoryItem *lastMessage = nullptr; HistoryItem *lastMessage = nullptr;
if (replyTo < 0) replyTo = _history->replyToId(); MsgId replyTo = (message.replyTo < 0) ? _history->replyToId() : 0;
while (command.isEmpty() && textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { while (command.isEmpty() && textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) {
FullMsgId newId(peerToChannel(hist->peer->id), clientMsgId()); FullMsgId newId(peerToChannel(history->peer->id), clientMsgId());
uint64 randomId = rand_value<uint64>(); uint64 randomId = rand_value<uint64>();
trimTextWithEntities(sendingText, sendingEntities); trimTextWithEntities(sendingText, &sendingEntities);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
App::historyRegSentData(randomId, hist->peer->id, sendingText); App::historyRegSentData(randomId, history->peer->id, sendingText);
MTPstring msgText(MTP_string(sendingText)); MTPstring msgText(MTP_string(sendingText));
MTPDmessage::Flags flags = newMessageFlags(hist->peer) | MTPDmessage::Flag::f_entities; // unread, out MTPDmessage::Flags flags = newMessageFlags(history->peer) | MTPDmessage::Flag::f_entities; // unread, out
MTPmessages_SendMessage::Flags sendFlags = 0; MTPmessages_SendMessage::Flags sendFlags = 0;
if (replyTo) { if (replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id; flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id; sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
} }
MTPMessageMedia media = MTP_messageMediaEmpty(); MTPMessageMedia media = MTP_messageMediaEmpty();
if (webPageId == CancelledWebPageId) { if (message.webPageId == CancelledWebPageId) {
sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage; sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage;
} else if (webPageId) { } else if (message.webPageId) {
WebPageData *page = App::webPage(webPageId); WebPageData *page = App::webPage(message.webPageId);
media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill))); media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill)));
flags |= MTPDmessage::Flag::f_media; flags |= MTPDmessage::Flag::f_media;
} }
bool channelPost = hist->peer->isChannel() && !hist->peer->isMegagroup() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast); bool channelPost = history->peer->isChannel() && !history->peer->isMegagroup() && history->peer->asChannel()->canPublish() && (history->peer->asChannel()->isBroadcast() || message.broadcast);
bool showFromName = !channelPost || hist->peer->asChannel()->addsSignature(); bool showFromName = !channelPost || history->peer->asChannel()->addsSignature();
bool silentPost = channelPost && silent; bool silentPost = channelPost && message.silent;
if (channelPost) { if (channelPost) {
sendFlags |= MTPmessages_SendMessage::Flag::f_broadcast; sendFlags |= MTPmessages_SendMessage::Flag::f_broadcast;
flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_views;
@ -1143,13 +1124,13 @@ void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo,
if (!sentEntities.c_vector().v.isEmpty()) { if (!sentEntities.c_vector().v.isEmpty()) {
sendFlags |= MTPmessages_SendMessage::Flag::f_entities; sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
} }
lastMessage = hist->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(newId.msg), MTP_int(showFromName ? MTP::authedId() : 0), peerToMTP(hist->peer->id), MTPnullFwdHeader, MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup, localEntities, MTP_int(1), MTPint()), NewMessageUnread); lastMessage = history->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(newId.msg), MTP_int(showFromName ? MTP::authedId() : 0), peerToMTP(history->peer->id), MTPnullFwdHeader, MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup, localEntities, MTP_int(1), MTPint()), NewMessageUnread);
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_flags(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, sentEntities), rpcDone(&MainWidget::sentUpdatesReceived, randomId), rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId); history->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_flags(sendFlags), history->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, sentEntities), rpcDone(&MainWidget::sentUpdatesReceived, randomId), rpcFail(&MainWidget::sendMessageFail), 0, 0, history->sendRequestId);
} }
hist->lastSentMsg = lastMessage; history->lastSentMsg = lastMessage;
finishForwarding(hist, broadcast, silent); finishForwarding(history, message.broadcast, message.silent);
executeParsedCommand(command); executeParsedCommand(command);
} }
@ -1728,7 +1709,8 @@ void MainWidget::dialogsCancelled() {
void MainWidget::serviceNotification(const QString &msg, const MTPMessageMedia &media) { void MainWidget::serviceNotification(const QString &msg, const MTPMessageMedia &media) {
MTPDmessage::Flags flags = MTPDmessage::Flag::f_unread | MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_from_id; MTPDmessage::Flags flags = MTPDmessage::Flag::f_unread | MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_from_id;
QString sendingText, leftText = msg; QString sendingText, leftText = msg;
EntitiesInText sendingEntities, leftEntities = textParseEntities(leftText, _historyTextNoMonoOptions.flags); EntitiesInText sendingEntities, leftEntities;
textParseEntities(leftText, _historyTextNoMonoOptions.flags, &leftEntities);
HistoryItem *item = 0; HistoryItem *item = 0;
while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) {
MTPVector<MTPMessageEntity> localEntities = linksToMTP(sendingEntities); MTPVector<MTPMessageEntity> localEntities = linksToMTP(sendingEntities);
@ -3817,6 +3799,107 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
update(); update();
} }
namespace {
bool fwdInfoDataLoaded(const MTPMessageFwdHeader &header) {
if (header.type() != mtpc_messageFwdHeader) {
return true;
}
auto &info = header.c_messageFwdHeader();
if (info.has_channel_id()) {
if (!App::channelLoaded(peerFromChannel(info.vchannel_id))) {
return false;
}
if (info.has_from_id() && !App::user(peerFromUser(info.vfrom_id), PeerData::MinimalLoaded)) {
return false;
}
} else {
if (info.has_from_id() && !App::userLoaded(peerFromUser(info.vfrom_id))) {
return false;
}
}
return true;
}
bool mentionUsersLoaded(const MTPVector<MTPMessageEntity> &entities) {
for_const (auto &entity, entities.c_vector().v) {
auto type = entity.type();
if (type == mtpc_messageEntityMentionName) {
if (!App::userLoaded(peerFromUser(entity.c_messageEntityMentionName().vuser_id))) {
return false;
}
} else if (type == mtpc_inputMessageEntityMentionName) {
auto &inputUser = entity.c_inputMessageEntityMentionName().vuser_id;
if (inputUser.type() == mtpc_inputUser) {
if (!App::userLoaded(peerFromUser(inputUser.c_inputUser().vuser_id))) {
return false;
}
}
}
}
return true;
}
enum class DataIsLoadedResult {
NotLoaded = 0,
FromNotLoaded = 1,
MentionNotLoaded = 2,
Ok = 3,
};
DataIsLoadedResult allDataLoadedForMessage(const MTPMessage &msg) {
switch (msg.type()) {
case mtpc_message: {
const MTPDmessage &d(msg.c_message());
if (!d.is_post() && d.has_from_id()) {
if (!App::userLoaded(peerFromUser(d.vfrom_id))) {
return DataIsLoadedResult::FromNotLoaded;
}
}
if (d.has_via_bot_id()) {
if (!App::userLoaded(peerFromUser(d.vvia_bot_id))) {
return DataIsLoadedResult::NotLoaded;
}
}
if (d.has_fwd_from() && !fwdInfoDataLoaded(d.vfwd_from)) {
return DataIsLoadedResult::NotLoaded;
}
if (d.has_entities() && !mentionUsersLoaded(d.ventities)) {
return DataIsLoadedResult::MentionNotLoaded;
}
} break;
case mtpc_messageService: {
const MTPDmessageService &d(msg.c_messageService());
if (!d.is_post() && d.has_from_id()) {
if (!App::userLoaded(peerFromUser(d.vfrom_id))) {
return DataIsLoadedResult::FromNotLoaded;
}
}
switch (d.vaction.type()) {
case mtpc_messageActionChatAddUser: {
for_const(const MTPint &userId, d.vaction.c_messageActionChatAddUser().vusers.c_vector().v) {
if (!App::userLoaded(peerFromUser(userId))) {
return DataIsLoadedResult::NotLoaded;
}
}
} break;
case mtpc_messageActionChatJoinedByLink: {
if (!App::userLoaded(peerFromUser(d.vaction.c_messageActionChatJoinedByLink().vinviter_id))) {
return DataIsLoadedResult::NotLoaded;
}
} break;
case mtpc_messageActionChatDeleteUser: {
if (!App::userLoaded(peerFromUser(d.vaction.c_messageActionChatDeleteUser().vuser_id))) {
return DataIsLoadedResult::NotLoaded;
}
} break;
}
} break;
}
return DataIsLoadedResult::Ok;
}
} // namespace
void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
switch (updates.type()) { switch (updates.type()) {
case mtpc_updates: { case mtpc_updates: {
@ -3863,21 +3946,13 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
case mtpc_updateShortMessage: { case mtpc_updateShortMessage: {
const auto &d(updates.c_updateShortMessage()); const auto &d(updates.c_updateShortMessage());
if (!App::userLoaded(d.vuser_id.v) || (d.has_via_bot_id() && !App::userLoaded(d.vvia_bot_id.v))) { if (!App::userLoaded(d.vuser_id.v)
|| (d.has_via_bot_id() && !App::userLoaded(d.vvia_bot_id.v))
|| (d.has_entities() && !mentionUsersLoaded(d.ventities))
|| (d.has_fwd_from() && !fwdInfoDataLoaded(d.vfwd_from))) {
MTP_LOG(0, ("getDifference { good - getting user for updateShortMessage }%1").arg(cTestMode() ? " TESTMODE" : "")); MTP_LOG(0, ("getDifference { good - getting user for updateShortMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
return getDifference(); return getDifference();
} }
if (d.has_fwd_from() && d.vfwd_from.type() == mtpc_messageFwdHeader) {
const auto &f(d.vfwd_from.c_messageFwdHeader());
if (f.has_from_id() && !App::userLoaded(f.vfrom_id.v)) {
MTP_LOG(0, ("getDifference { good - getting user for updateShortMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
return getDifference();
}
if (f.has_channel_id() && !App::channelLoaded(f.vchannel_id.v)) {
MTP_LOG(0, ("getDifference { good - getting user for updateShortMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
return getDifference();
}
}
if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) { if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) {
return; return;
} }
@ -3897,22 +3972,15 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
case mtpc_updateShortChatMessage: { case mtpc_updateShortChatMessage: {
const auto &d(updates.c_updateShortChatMessage()); const auto &d(updates.c_updateShortChatMessage());
bool noFrom = !App::userLoaded(d.vfrom_id.v); bool noFrom = !App::userLoaded(d.vfrom_id.v);
if (!App::chatLoaded(d.vchat_id.v) || noFrom || (d.has_via_bot_id() && !App::userLoaded(d.vvia_bot_id.v))) { if (!App::chatLoaded(d.vchat_id.v)
|| noFrom
|| (d.has_via_bot_id() && !App::userLoaded(d.vvia_bot_id.v))
|| (d.has_entities() && !mentionUsersLoaded(d.ventities))
|| (d.has_fwd_from() && !fwdInfoDataLoaded(d.vfwd_from))) {
MTP_LOG(0, ("getDifference { good - getting user for updateShortChatMessage }%1").arg(cTestMode() ? " TESTMODE" : "")); MTP_LOG(0, ("getDifference { good - getting user for updateShortChatMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
if (noFrom && App::api()) App::api()->requestFullPeer(App::chatLoaded(d.vchat_id.v)); if (noFrom && App::api()) App::api()->requestFullPeer(App::chatLoaded(d.vchat_id.v));
return getDifference(); return getDifference();
} }
if (d.has_fwd_from() && d.vfwd_from.type() == mtpc_messageFwdHeader) {
const auto &f(d.vfwd_from.c_messageFwdHeader());
if (f.has_from_id() && !App::userLoaded(f.vfrom_id.v)) {
MTP_LOG(0, ("getDifference { good - getting user for updateShortChatMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
return getDifference();
}
if (f.has_channel_id() && !App::channelLoaded(f.vchannel_id.v)) {
MTP_LOG(0, ("getDifference { good - getting user for updateShortChatMessage }%1").arg(cTestMode() ? " TESTMODE" : ""));
return getDifference();
}
}
if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) { if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) {
return; return;
} }
@ -3938,8 +4006,12 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
if (peerId) { if (peerId) {
if (HistoryItem *item = App::histItemById(peerToChannel(peerId), d.vid.v)) { if (auto item = App::histItemById(peerToChannel(peerId), d.vid.v)) {
item->setText(text, d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText()); if (d.has_entities() && !mentionUsersLoaded(d.ventities)) {
api()->requestMessageData(item->history()->peer->asChannel(), item->id, nullptr);
}
auto entities = d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText();
item->setText({ text, entities });
item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr); item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr);
item->addToOverview(AddToOverviewNew); item->addToOverview(AddToOverviewNew);
} }
@ -3962,73 +4034,6 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
} }
} }
namespace {
enum class DataIsLoadedResult {
NotLoaded = 0,
FromNotLoaded = 1,
Ok = 2,
};
DataIsLoadedResult allDataLoadedForMessage(const MTPMessage &msg) {
switch (msg.type()) {
case mtpc_message: {
const MTPDmessage &d(msg.c_message());
if (!d.is_post() && d.has_from_id()) {
if (!App::userLoaded(peerFromUser(d.vfrom_id))) {
return DataIsLoadedResult::FromNotLoaded;
}
}
if (d.has_via_bot_id()) {
if (!App::userLoaded(peerFromUser(d.vvia_bot_id))) {
return DataIsLoadedResult::NotLoaded;
}
}
if (d.has_fwd_from() && d.vfwd_from.type() == mtpc_messageFwdHeader) {
ChannelId fromChannelId = d.vfwd_from.c_messageFwdHeader().vchannel_id.v;
if (fromChannelId) {
if (!App::channelLoaded(peerFromChannel(fromChannelId))) {
return DataIsLoadedResult::NotLoaded;
}
} else {
if (!App::userLoaded(peerFromUser(d.vfwd_from.c_messageFwdHeader().vfrom_id))) {
return DataIsLoadedResult::NotLoaded;
}
}
}
} break;
case mtpc_messageService: {
const MTPDmessageService &d(msg.c_messageService());
if (!d.is_post() && d.has_from_id()) {
if (!App::userLoaded(peerFromUser(d.vfrom_id))) {
return DataIsLoadedResult::FromNotLoaded;
}
}
switch (d.vaction.type()) {
case mtpc_messageActionChatAddUser: {
for_const(const MTPint &userId, d.vaction.c_messageActionChatAddUser().vusers.c_vector().v) {
if (!App::userLoaded(peerFromUser(userId))) {
return DataIsLoadedResult::NotLoaded;
}
}
} break;
case mtpc_messageActionChatJoinedByLink: {
if (!App::userLoaded(peerFromUser(d.vaction.c_messageActionChatJoinedByLink().vinviter_id))) {
return DataIsLoadedResult::NotLoaded;
}
} break;
case mtpc_messageActionChatDeleteUser: {
if (!App::userLoaded(peerFromUser(d.vaction.c_messageActionChatDeleteUser().vuser_id))) {
return DataIsLoadedResult::NotLoaded;
}
} break;
}
} break;
}
return DataIsLoadedResult::Ok;
}
} // namespace
void MainWidget::feedUpdate(const MTPUpdate &update) { void MainWidget::feedUpdate(const MTPUpdate &update) {
if (!MTP::authedId()) return; if (!MTP::authedId()) return;

View File

@ -281,7 +281,15 @@ public:
Dialogs::IndexedList *contactsList(); Dialogs::IndexedList *contactsList();
Dialogs::IndexedList *dialogsList(); Dialogs::IndexedList *dialogsList();
void sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast, bool silent, WebPageId webPageId = 0); struct MessageToSend {
History *history = nullptr;
TextWithTags textWithTags;
MsgId replyTo = 0;
bool broadcast = false;
bool silent = false;
WebPageId webPageId = 0;
};
void sendMessage(const MessageToSend &message);
void saveRecentHashtags(const QString &text); void saveRecentHashtags(const QString &text);
void readServerHistory(History *history, bool force = true); void readServerHistory(History *history, bool force = true);

View File

@ -230,6 +230,8 @@ public:
void updateUnreadCounter(); void updateUnreadCounter();
QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon);
bool contentOverlapped(const QRect &globalRect); bool contentOverlapped(const QRect &globalRect);
bool contentOverlapped(QWidget *w, QPaintEvent *e) { bool contentOverlapped(QWidget *w, QPaintEvent *e) {
return contentOverlapped(QRect(w->mapToGlobal(e->rect().topLeft()), e->rect().size())); return contentOverlapped(QRect(w->mapToGlobal(e->rect().topLeft()), e->rect().size()));
@ -282,8 +284,6 @@ public slots:
void onLogoutSure(); void onLogoutSure();
void updateGlobalMenu(); // for OS X top menu void updateGlobalMenu(); // for OS X top menu
QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon);
void notifyUpdateAllPhotos(); void notifyUpdateAllPhotos();
void app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); void app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button);

View File

@ -912,7 +912,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) {
_caption = Text(); _caption = Text();
if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : nullptr) { if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : nullptr) {
if (HistoryPhoto *photoMsg = dynamic_cast<HistoryPhoto*>(itemMsg->getMedia())) { if (HistoryPhoto *photoMsg = dynamic_cast<HistoryPhoto*>(itemMsg->getMedia())) {
_caption.setText(st::mvCaptionFont, photoMsg->getCaption(), (item->author()->isUser() && item->author()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions); _caption.setMarkedText(st::mvCaptionFont, photoMsg->getCaption(), (item->author()->isUser() && item->author()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions);
} }
} }

View File

@ -414,7 +414,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true id:int ip_address:string port:int = DcOption; dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true id:int ip_address:string port:int = DcOption;
config#317ceef4 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int disabled_features:Vector<DisabledFeature> = Config; config#c9411388 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int disabled_features:Vector<DisabledFeature> = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -587,6 +587,8 @@ messageEntityItalic#826f8b60 offset:int length:int = MessageEntity;
messageEntityCode#28a20571 offset:int length:int = MessageEntity; messageEntityCode#28a20571 offset:int length:int = MessageEntity;
messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity; messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity; messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity;
inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel; inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; inputChannel#afeb712e channel_id:int access_hash:long = InputChannel;
@ -677,6 +679,21 @@ inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotIn
inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM; inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM;
messages.peerDialogs#3371c354 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> state:updates.State = messages.PeerDialogs;
topPeer#edcdc05b peer:Peer rating:double = TopPeer;
topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;
topPeerCategoryBotsInline#148677e2 = TopPeerCategory;
topPeerCategoryCorrespondents#637b7ed = TopPeerCategory;
topPeerCategoryGroups#bd17a14a = TopPeerCategory;
topPeerCategoryChannels#161d9628 = TopPeerCategory;
topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<TopPeer> = TopPeerCategoryPeers;
contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -742,6 +759,8 @@ contacts.exportCard#84e53737 = Vector<int>;
contacts.importCard#4fe196fe export_card:Vector<int> = User; contacts.importCard#4fe196fe export_card:Vector<int> = User;
contacts.search#11f812d8 q:string limit:int = contacts.Found; contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages; messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
messages.getDialogs#6b47f94d offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs; messages.getDialogs#6b47f94d offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
@ -806,6 +825,7 @@ messages.editMessage#ce91e4ca flags:# no_webpage:flags.1?true peer:InputPeer id:
messages.editInlineBotMessage#130c2c85 flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool; messages.editInlineBotMessage#130c2c85 flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
messages.getBotCallbackAnswer#a6e94f04 peer:InputPeer msg_id:int data:bytes = messages.BotCallbackAnswer; messages.getBotCallbackAnswer#a6e94f04 peer:InputPeer msg_id:int data:bytes = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#481c591a flags:# alert:flags.1?true query_id:long message:flags.0?string = Bool; messages.setBotCallbackAnswer#481c591a flags:# alert:flags.1?true query_id:long message:flags.0?string = Bool;
messages.getPeerDialogs#19250887 peer:Vector<InputPeer> = messages.PeerDialogs;
updates.getState#edd4882a = updates.State; updates.getState#edd4882a = updates.State;
updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference; updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference;
@ -859,4 +879,4 @@ channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessag
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates; channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
// LAYER 51 // LAYER 52

View File

@ -3314,7 +3314,8 @@ void _serialize_config(MTPStringLogger &to, int32 stage, int32 lev, Types &types
case 16: to.add(" push_chat_limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 16: to.add(" push_chat_limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 17: to.add(" saved_gifs_limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 17: to.add(" saved_gifs_limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 18: to.add(" edit_time_limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 18: to.add(" edit_time_limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 19: to.add(" disabled_features: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 19: to.add(" rating_e_decay: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 20: to.add(" disabled_features: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
} }
} }
@ -4768,6 +4769,36 @@ void _serialize_messageEntityTextUrl(MTPStringLogger &to, int32 stage, int32 lev
} }
} }
void _serialize_messageEntityMentionName(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messageEntityMentionName");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" offset: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" length: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_inputMessageEntityMentionName(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ inputMessageEntityMentionName");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" offset: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" length: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" user_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_inputChannelEmpty(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) { void _serialize_inputChannelEmpty(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ inputChannelEmpty }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); to.add("{ inputChannelEmpty }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
} }
@ -5622,6 +5653,91 @@ void _serialize_inlineBotSwitchPM(MTPStringLogger &to, int32 stage, int32 lev, T
} }
} }
void _serialize_messages_peerDialogs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_peerDialogs");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" dialogs: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" messages: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" chats: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" users: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" state: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_topPeer(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ topPeer");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" rating: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_topPeerCategoryBotsPM(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ topPeerCategoryBotsPM }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_topPeerCategoryBotsInline(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ topPeerCategoryBotsInline }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_topPeerCategoryCorrespondents(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ topPeerCategoryCorrespondents }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_topPeerCategoryGroups(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ topPeerCategoryGroups }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_topPeerCategoryChannels(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ topPeerCategoryChannels }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_topPeerCategoryPeers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ topPeerCategoryPeers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" category: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" count: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" peers: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_contacts_topPeersNotModified(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ contacts_topPeersNotModified }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_contacts_topPeers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ contacts_topPeers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" categories: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" chats: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" users: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_req_pq(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) { void _serialize_req_pq(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) { if (stage) {
to.add(",\n").addSpaces(lev); to.add(",\n").addSpaces(lev);
@ -5999,6 +6115,20 @@ void _serialize_contacts_unblock(MTPStringLogger &to, int32 stage, int32 lev, Ty
} }
} }
void _serialize_contacts_resetTopPeerRating(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ contacts_resetTopPeerRating");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" category: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_setTyping(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) { void _serialize_messages_setTyping(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) { if (stage) {
to.add(",\n").addSpaces(lev); to.add(",\n").addSpaces(lev);
@ -6826,6 +6956,29 @@ void _serialize_contacts_resolveUsername(MTPStringLogger &to, int32 stage, int32
} }
} }
void _serialize_contacts_getTopPeers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPcontacts_getTopPeers::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ contacts_getTopPeers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" correspondents: "); ++stages.back(); if (flag & MTPcontacts_getTopPeers::Flag::f_correspondents) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" bots_pm: "); ++stages.back(); if (flag & MTPcontacts_getTopPeers::Flag::f_bots_pm) { to.add("YES [ BY BIT 1 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
case 3: to.add(" bots_inline: "); ++stages.back(); if (flag & MTPcontacts_getTopPeers::Flag::f_bots_inline) { to.add("YES [ BY BIT 2 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break;
case 4: to.add(" groups: "); ++stages.back(); if (flag & MTPcontacts_getTopPeers::Flag::f_groups) { to.add("YES [ BY BIT 10 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 10 IN FIELD flags ]"); } break;
case 5: to.add(" channels: "); ++stages.back(); if (flag & MTPcontacts_getTopPeers::Flag::f_channels) { to.add("YES [ BY BIT 15 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 15 IN FIELD flags ]"); } break;
case 6: to.add(" offset: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 7: to.add(" limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 8: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_getMessages(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) { void _serialize_messages_getMessages(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) { if (stage) {
to.add(",\n").addSpaces(lev); to.add(",\n").addSpaces(lev);
@ -7883,6 +8036,19 @@ void _serialize_messages_getBotCallbackAnswer(MTPStringLogger &to, int32 stage,
} }
} }
void _serialize_messages_getPeerDialogs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_getPeerDialogs");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" peer: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_updates_getState(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) { void _serialize_updates_getState(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ updates_getState }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); to.add("{ updates_getState }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
} }
@ -8520,6 +8686,8 @@ namespace {
_serializers.insert(mtpc_messageEntityCode, _serialize_messageEntityCode); _serializers.insert(mtpc_messageEntityCode, _serialize_messageEntityCode);
_serializers.insert(mtpc_messageEntityPre, _serialize_messageEntityPre); _serializers.insert(mtpc_messageEntityPre, _serialize_messageEntityPre);
_serializers.insert(mtpc_messageEntityTextUrl, _serialize_messageEntityTextUrl); _serializers.insert(mtpc_messageEntityTextUrl, _serialize_messageEntityTextUrl);
_serializers.insert(mtpc_messageEntityMentionName, _serialize_messageEntityMentionName);
_serializers.insert(mtpc_inputMessageEntityMentionName, _serialize_inputMessageEntityMentionName);
_serializers.insert(mtpc_inputChannelEmpty, _serialize_inputChannelEmpty); _serializers.insert(mtpc_inputChannelEmpty, _serialize_inputChannelEmpty);
_serializers.insert(mtpc_inputChannel, _serialize_inputChannel); _serializers.insert(mtpc_inputChannel, _serialize_inputChannel);
_serializers.insert(mtpc_contacts_resolvedPeer, _serialize_contacts_resolvedPeer); _serializers.insert(mtpc_contacts_resolvedPeer, _serialize_contacts_resolvedPeer);
@ -8581,6 +8749,16 @@ namespace {
_serializers.insert(mtpc_messages_messageEditData, _serialize_messages_messageEditData); _serializers.insert(mtpc_messages_messageEditData, _serialize_messages_messageEditData);
_serializers.insert(mtpc_inputBotInlineMessageID, _serialize_inputBotInlineMessageID); _serializers.insert(mtpc_inputBotInlineMessageID, _serialize_inputBotInlineMessageID);
_serializers.insert(mtpc_inlineBotSwitchPM, _serialize_inlineBotSwitchPM); _serializers.insert(mtpc_inlineBotSwitchPM, _serialize_inlineBotSwitchPM);
_serializers.insert(mtpc_messages_peerDialogs, _serialize_messages_peerDialogs);
_serializers.insert(mtpc_topPeer, _serialize_topPeer);
_serializers.insert(mtpc_topPeerCategoryBotsPM, _serialize_topPeerCategoryBotsPM);
_serializers.insert(mtpc_topPeerCategoryBotsInline, _serialize_topPeerCategoryBotsInline);
_serializers.insert(mtpc_topPeerCategoryCorrespondents, _serialize_topPeerCategoryCorrespondents);
_serializers.insert(mtpc_topPeerCategoryGroups, _serialize_topPeerCategoryGroups);
_serializers.insert(mtpc_topPeerCategoryChannels, _serialize_topPeerCategoryChannels);
_serializers.insert(mtpc_topPeerCategoryPeers, _serialize_topPeerCategoryPeers);
_serializers.insert(mtpc_contacts_topPeersNotModified, _serialize_contacts_topPeersNotModified);
_serializers.insert(mtpc_contacts_topPeers, _serialize_contacts_topPeers);
_serializers.insert(mtpc_req_pq, _serialize_req_pq); _serializers.insert(mtpc_req_pq, _serialize_req_pq);
_serializers.insert(mtpc_req_DH_params, _serialize_req_DH_params); _serializers.insert(mtpc_req_DH_params, _serialize_req_DH_params);
@ -8611,6 +8789,7 @@ namespace {
_serializers.insert(mtpc_contacts_deleteContacts, _serialize_contacts_deleteContacts); _serializers.insert(mtpc_contacts_deleteContacts, _serialize_contacts_deleteContacts);
_serializers.insert(mtpc_contacts_block, _serialize_contacts_block); _serializers.insert(mtpc_contacts_block, _serialize_contacts_block);
_serializers.insert(mtpc_contacts_unblock, _serialize_contacts_unblock); _serializers.insert(mtpc_contacts_unblock, _serialize_contacts_unblock);
_serializers.insert(mtpc_contacts_resetTopPeerRating, _serialize_contacts_resetTopPeerRating);
_serializers.insert(mtpc_messages_setTyping, _serialize_messages_setTyping); _serializers.insert(mtpc_messages_setTyping, _serialize_messages_setTyping);
_serializers.insert(mtpc_messages_reportSpam, _serialize_messages_reportSpam); _serializers.insert(mtpc_messages_reportSpam, _serialize_messages_reportSpam);
_serializers.insert(mtpc_messages_hideReportSpam, _serialize_messages_hideReportSpam); _serializers.insert(mtpc_messages_hideReportSpam, _serialize_messages_hideReportSpam);
@ -8673,6 +8852,7 @@ namespace {
_serializers.insert(mtpc_messages_getMessagesViews, _serialize_messages_getMessagesViews); _serializers.insert(mtpc_messages_getMessagesViews, _serialize_messages_getMessagesViews);
_serializers.insert(mtpc_contacts_search, _serialize_contacts_search); _serializers.insert(mtpc_contacts_search, _serialize_contacts_search);
_serializers.insert(mtpc_contacts_resolveUsername, _serialize_contacts_resolveUsername); _serializers.insert(mtpc_contacts_resolveUsername, _serialize_contacts_resolveUsername);
_serializers.insert(mtpc_contacts_getTopPeers, _serialize_contacts_getTopPeers);
_serializers.insert(mtpc_messages_getMessages, _serialize_messages_getMessages); _serializers.insert(mtpc_messages_getMessages, _serialize_messages_getMessages);
_serializers.insert(mtpc_messages_getHistory, _serialize_messages_getHistory); _serializers.insert(mtpc_messages_getHistory, _serialize_messages_getHistory);
_serializers.insert(mtpc_messages_search, _serialize_messages_search); _serializers.insert(mtpc_messages_search, _serialize_messages_search);
@ -8743,6 +8923,7 @@ namespace {
_serializers.insert(mtpc_messages_getInlineBotResults, _serialize_messages_getInlineBotResults); _serializers.insert(mtpc_messages_getInlineBotResults, _serialize_messages_getInlineBotResults);
_serializers.insert(mtpc_messages_getMessageEditData, _serialize_messages_getMessageEditData); _serializers.insert(mtpc_messages_getMessageEditData, _serialize_messages_getMessageEditData);
_serializers.insert(mtpc_messages_getBotCallbackAnswer, _serialize_messages_getBotCallbackAnswer); _serializers.insert(mtpc_messages_getBotCallbackAnswer, _serialize_messages_getBotCallbackAnswer);
_serializers.insert(mtpc_messages_getPeerDialogs, _serialize_messages_getPeerDialogs);
_serializers.insert(mtpc_updates_getState, _serialize_updates_getState); _serializers.insert(mtpc_updates_getState, _serialize_updates_getState);
_serializers.insert(mtpc_updates_getDifference, _serialize_updates_getDifference); _serializers.insert(mtpc_updates_getDifference, _serialize_updates_getDifference);
_serializers.insert(mtpc_updates_getChannelDifference, _serialize_updates_getChannelDifference); _serializers.insert(mtpc_updates_getChannelDifference, _serialize_updates_getChannelDifference);

View File

@ -30,7 +30,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
namespace MTP { namespace MTP {
namespace internal { namespace internal {
static constexpr mtpPrime CurrentLayer = 51; static constexpr mtpPrime CurrentLayer = 52;
class TypeCreator; class TypeCreator;
@ -302,7 +302,7 @@ enum {
mtpc_photos_photo = 0x20212ca8, mtpc_photos_photo = 0x20212ca8,
mtpc_upload_file = 0x96a18d5, mtpc_upload_file = 0x96a18d5,
mtpc_dcOption = 0x5d8c6cc, mtpc_dcOption = 0x5d8c6cc,
mtpc_config = 0x317ceef4, mtpc_config = 0xc9411388,
mtpc_nearestDc = 0x8e1a1775, mtpc_nearestDc = 0x8e1a1775,
mtpc_help_appUpdate = 0x8987f311, mtpc_help_appUpdate = 0x8987f311,
mtpc_help_noAppUpdate = 0xc45a6536, mtpc_help_noAppUpdate = 0xc45a6536,
@ -426,6 +426,8 @@ enum {
mtpc_messageEntityCode = 0x28a20571, mtpc_messageEntityCode = 0x28a20571,
mtpc_messageEntityPre = 0x73924be0, mtpc_messageEntityPre = 0x73924be0,
mtpc_messageEntityTextUrl = 0x76a6d327, mtpc_messageEntityTextUrl = 0x76a6d327,
mtpc_messageEntityMentionName = 0x352dca58,
mtpc_inputMessageEntityMentionName = 0x208e68c9,
mtpc_inputChannelEmpty = 0xee8c1e86, mtpc_inputChannelEmpty = 0xee8c1e86,
mtpc_inputChannel = 0xafeb712e, mtpc_inputChannel = 0xafeb712e,
mtpc_contacts_resolvedPeer = 0x7f077ad9, mtpc_contacts_resolvedPeer = 0x7f077ad9,
@ -487,6 +489,16 @@ enum {
mtpc_messages_messageEditData = 0x26b5dde6, mtpc_messages_messageEditData = 0x26b5dde6,
mtpc_inputBotInlineMessageID = 0x890c3d89, mtpc_inputBotInlineMessageID = 0x890c3d89,
mtpc_inlineBotSwitchPM = 0x3c20629f, mtpc_inlineBotSwitchPM = 0x3c20629f,
mtpc_messages_peerDialogs = 0x3371c354,
mtpc_topPeer = 0xedcdc05b,
mtpc_topPeerCategoryBotsPM = 0xab661b5b,
mtpc_topPeerCategoryBotsInline = 0x148677e2,
mtpc_topPeerCategoryCorrespondents = 0x637b7ed,
mtpc_topPeerCategoryGroups = 0xbd17a14a,
mtpc_topPeerCategoryChannels = 0x161d9628,
mtpc_topPeerCategoryPeers = 0xfb834291,
mtpc_contacts_topPeersNotModified = 0xde266ef5,
mtpc_contacts_topPeers = 0x70b772a8,
mtpc_invokeAfterMsg = 0xcb9f372d, mtpc_invokeAfterMsg = 0xcb9f372d,
mtpc_invokeAfterMsgs = 0x3dc4b4f0, mtpc_invokeAfterMsgs = 0x3dc4b4f0,
mtpc_initConnection = 0x69796de9, mtpc_initConnection = 0x69796de9,
@ -546,6 +558,8 @@ enum {
mtpc_contacts_importCard = 0x4fe196fe, mtpc_contacts_importCard = 0x4fe196fe,
mtpc_contacts_search = 0x11f812d8, mtpc_contacts_search = 0x11f812d8,
mtpc_contacts_resolveUsername = 0xf93ccba3, mtpc_contacts_resolveUsername = 0xf93ccba3,
mtpc_contacts_getTopPeers = 0xd4982db5,
mtpc_contacts_resetTopPeerRating = 0x1ae373ac,
mtpc_messages_getMessages = 0x4222fa74, mtpc_messages_getMessages = 0x4222fa74,
mtpc_messages_getDialogs = 0x6b47f94d, mtpc_messages_getDialogs = 0x6b47f94d,
mtpc_messages_getHistory = 0xafa92846, mtpc_messages_getHistory = 0xafa92846,
@ -609,6 +623,7 @@ enum {
mtpc_messages_editInlineBotMessage = 0x130c2c85, mtpc_messages_editInlineBotMessage = 0x130c2c85,
mtpc_messages_getBotCallbackAnswer = 0xa6e94f04, mtpc_messages_getBotCallbackAnswer = 0xa6e94f04,
mtpc_messages_setBotCallbackAnswer = 0x481c591a, mtpc_messages_setBotCallbackAnswer = 0x481c591a,
mtpc_messages_getPeerDialogs = 0x19250887,
mtpc_updates_getState = 0xedd4882a, mtpc_updates_getState = 0xedd4882a,
mtpc_updates_getDifference = 0xa041495, mtpc_updates_getDifference = 0xa041495,
mtpc_updates_getChannelDifference = 0xbb32d7c0, mtpc_updates_getChannelDifference = 0xbb32d7c0,
@ -1217,6 +1232,8 @@ class MTPDmessageEntityItalic;
class MTPDmessageEntityCode; class MTPDmessageEntityCode;
class MTPDmessageEntityPre; class MTPDmessageEntityPre;
class MTPDmessageEntityTextUrl; class MTPDmessageEntityTextUrl;
class MTPDmessageEntityMentionName;
class MTPDinputMessageEntityMentionName;
class MTPinputChannel; class MTPinputChannel;
class MTPDinputChannel; class MTPDinputChannel;
@ -1321,6 +1338,20 @@ class MTPDinputBotInlineMessageID;
class MTPinlineBotSwitchPM; class MTPinlineBotSwitchPM;
class MTPDinlineBotSwitchPM; class MTPDinlineBotSwitchPM;
class MTPmessages_peerDialogs;
class MTPDmessages_peerDialogs;
class MTPtopPeer;
class MTPDtopPeer;
class MTPtopPeerCategory;
class MTPtopPeerCategoryPeers;
class MTPDtopPeerCategoryPeers;
class MTPcontacts_topPeers;
class MTPDcontacts_topPeers;
// Boxed types definitions // Boxed types definitions
typedef MTPBoxed<MTPresPQ> MTPResPQ; typedef MTPBoxed<MTPresPQ> MTPResPQ;
@ -1490,6 +1521,11 @@ typedef MTPBoxed<MTPmessages_botCallbackAnswer> MTPmessages_BotCallbackAnswer;
typedef MTPBoxed<MTPmessages_messageEditData> MTPmessages_MessageEditData; typedef MTPBoxed<MTPmessages_messageEditData> MTPmessages_MessageEditData;
typedef MTPBoxed<MTPinputBotInlineMessageID> MTPInputBotInlineMessageID; typedef MTPBoxed<MTPinputBotInlineMessageID> MTPInputBotInlineMessageID;
typedef MTPBoxed<MTPinlineBotSwitchPM> MTPInlineBotSwitchPM; typedef MTPBoxed<MTPinlineBotSwitchPM> MTPInlineBotSwitchPM;
typedef MTPBoxed<MTPmessages_peerDialogs> MTPmessages_PeerDialogs;
typedef MTPBoxed<MTPtopPeer> MTPTopPeer;
typedef MTPBoxed<MTPtopPeerCategory> MTPTopPeerCategory;
typedef MTPBoxed<MTPtopPeerCategoryPeers> MTPTopPeerCategoryPeers;
typedef MTPBoxed<MTPcontacts_topPeers> MTPcontacts_TopPeers;
// Type classes definitions // Type classes definitions
@ -8089,6 +8125,30 @@ public:
return *(const MTPDmessageEntityTextUrl*)data; return *(const MTPDmessageEntityTextUrl*)data;
} }
MTPDmessageEntityMentionName &_messageEntityMentionName() {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_messageEntityMentionName) throw mtpErrorWrongTypeId(_type, mtpc_messageEntityMentionName);
split();
return *(MTPDmessageEntityMentionName*)data;
}
const MTPDmessageEntityMentionName &c_messageEntityMentionName() const {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_messageEntityMentionName) throw mtpErrorWrongTypeId(_type, mtpc_messageEntityMentionName);
return *(const MTPDmessageEntityMentionName*)data;
}
MTPDinputMessageEntityMentionName &_inputMessageEntityMentionName() {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_inputMessageEntityMentionName) throw mtpErrorWrongTypeId(_type, mtpc_inputMessageEntityMentionName);
split();
return *(MTPDinputMessageEntityMentionName*)data;
}
const MTPDinputMessageEntityMentionName &c_inputMessageEntityMentionName() const {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_inputMessageEntityMentionName) throw mtpErrorWrongTypeId(_type, mtpc_inputMessageEntityMentionName);
return *(const MTPDinputMessageEntityMentionName*)data;
}
uint32 innerLength() const; uint32 innerLength() const;
mtpTypeId type() const; mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons); void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
@ -8109,6 +8169,8 @@ private:
explicit MTPmessageEntity(MTPDmessageEntityCode *_data); explicit MTPmessageEntity(MTPDmessageEntityCode *_data);
explicit MTPmessageEntity(MTPDmessageEntityPre *_data); explicit MTPmessageEntity(MTPDmessageEntityPre *_data);
explicit MTPmessageEntity(MTPDmessageEntityTextUrl *_data); explicit MTPmessageEntity(MTPDmessageEntityTextUrl *_data);
explicit MTPmessageEntity(MTPDmessageEntityMentionName *_data);
explicit MTPmessageEntity(MTPDinputMessageEntityMentionName *_data);
friend class MTP::internal::TypeCreator; friend class MTP::internal::TypeCreator;
@ -9315,6 +9377,160 @@ private:
}; };
typedef MTPBoxed<MTPinlineBotSwitchPM> MTPInlineBotSwitchPM; typedef MTPBoxed<MTPinlineBotSwitchPM> MTPInlineBotSwitchPM;
class MTPmessages_peerDialogs : private mtpDataOwner {
public:
MTPmessages_peerDialogs();
MTPmessages_peerDialogs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_peerDialogs) : mtpDataOwner(0) {
read(from, end, cons);
}
MTPDmessages_peerDialogs &_messages_peerDialogs() {
if (!data) throw mtpErrorUninitialized();
split();
return *(MTPDmessages_peerDialogs*)data;
}
const MTPDmessages_peerDialogs &c_messages_peerDialogs() const {
if (!data) throw mtpErrorUninitialized();
return *(const MTPDmessages_peerDialogs*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_peerDialogs);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPmessages_peerDialogs(MTPDmessages_peerDialogs *_data);
friend class MTP::internal::TypeCreator;
};
typedef MTPBoxed<MTPmessages_peerDialogs> MTPmessages_PeerDialogs;
class MTPtopPeer : private mtpDataOwner {
public:
MTPtopPeer();
MTPtopPeer(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_topPeer) : mtpDataOwner(0) {
read(from, end, cons);
}
MTPDtopPeer &_topPeer() {
if (!data) throw mtpErrorUninitialized();
split();
return *(MTPDtopPeer*)data;
}
const MTPDtopPeer &c_topPeer() const {
if (!data) throw mtpErrorUninitialized();
return *(const MTPDtopPeer*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_topPeer);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPtopPeer(MTPDtopPeer *_data);
friend class MTP::internal::TypeCreator;
};
typedef MTPBoxed<MTPtopPeer> MTPTopPeer;
class MTPtopPeerCategory {
public:
MTPtopPeerCategory() : _type(0) {
}
MTPtopPeerCategory(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : _type(0) {
read(from, end, cons);
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPtopPeerCategory(mtpTypeId type);
friend class MTP::internal::TypeCreator;
mtpTypeId _type;
};
typedef MTPBoxed<MTPtopPeerCategory> MTPTopPeerCategory;
class MTPtopPeerCategoryPeers : private mtpDataOwner {
public:
MTPtopPeerCategoryPeers();
MTPtopPeerCategoryPeers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_topPeerCategoryPeers) : mtpDataOwner(0) {
read(from, end, cons);
}
MTPDtopPeerCategoryPeers &_topPeerCategoryPeers() {
if (!data) throw mtpErrorUninitialized();
split();
return *(MTPDtopPeerCategoryPeers*)data;
}
const MTPDtopPeerCategoryPeers &c_topPeerCategoryPeers() const {
if (!data) throw mtpErrorUninitialized();
return *(const MTPDtopPeerCategoryPeers*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_topPeerCategoryPeers);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPtopPeerCategoryPeers(MTPDtopPeerCategoryPeers *_data);
friend class MTP::internal::TypeCreator;
};
typedef MTPBoxed<MTPtopPeerCategoryPeers> MTPTopPeerCategoryPeers;
class MTPcontacts_topPeers : private mtpDataOwner {
public:
MTPcontacts_topPeers() : mtpDataOwner(0), _type(0) {
}
MTPcontacts_topPeers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : mtpDataOwner(0), _type(0) {
read(from, end, cons);
}
MTPDcontacts_topPeers &_contacts_topPeers() {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_contacts_topPeers) throw mtpErrorWrongTypeId(_type, mtpc_contacts_topPeers);
split();
return *(MTPDcontacts_topPeers*)data;
}
const MTPDcontacts_topPeers &c_contacts_topPeers() const {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_contacts_topPeers) throw mtpErrorWrongTypeId(_type, mtpc_contacts_topPeers);
return *(const MTPDcontacts_topPeers*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPcontacts_topPeers(mtpTypeId type);
explicit MTPcontacts_topPeers(MTPDcontacts_topPeers *_data);
friend class MTP::internal::TypeCreator;
mtpTypeId _type;
};
typedef MTPBoxed<MTPcontacts_topPeers> MTPcontacts_TopPeers;
// Type constructors with data // Type constructors with data
class MTPDresPQ : public mtpDataImpl<MTPDresPQ> { class MTPDresPQ : public mtpDataImpl<MTPDresPQ> {
@ -12042,7 +12258,7 @@ class MTPDconfig : public mtpDataImpl<MTPDconfig> {
public: public:
MTPDconfig() { MTPDconfig() {
} }
MTPDconfig(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector<MTPDcOption> &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, MTPint _edit_time_limit, const MTPVector<MTPDisabledFeature> &_disabled_features) : vdate(_date), vexpires(_expires), vtest_mode(_test_mode), vthis_dc(_this_dc), vdc_options(_dc_options), vchat_size_max(_chat_size_max), vmegagroup_size_max(_megagroup_size_max), vforwarded_count_max(_forwarded_count_max), vonline_update_period_ms(_online_update_period_ms), voffline_blur_timeout_ms(_offline_blur_timeout_ms), voffline_idle_timeout_ms(_offline_idle_timeout_ms), vonline_cloud_timeout_ms(_online_cloud_timeout_ms), vnotify_cloud_delay_ms(_notify_cloud_delay_ms), vnotify_default_delay_ms(_notify_default_delay_ms), vchat_big_size(_chat_big_size), vpush_chat_period_ms(_push_chat_period_ms), vpush_chat_limit(_push_chat_limit), vsaved_gifs_limit(_saved_gifs_limit), vedit_time_limit(_edit_time_limit), vdisabled_features(_disabled_features) { MTPDconfig(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector<MTPDcOption> &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, MTPint _edit_time_limit, MTPint _rating_e_decay, const MTPVector<MTPDisabledFeature> &_disabled_features) : vdate(_date), vexpires(_expires), vtest_mode(_test_mode), vthis_dc(_this_dc), vdc_options(_dc_options), vchat_size_max(_chat_size_max), vmegagroup_size_max(_megagroup_size_max), vforwarded_count_max(_forwarded_count_max), vonline_update_period_ms(_online_update_period_ms), voffline_blur_timeout_ms(_offline_blur_timeout_ms), voffline_idle_timeout_ms(_offline_idle_timeout_ms), vonline_cloud_timeout_ms(_online_cloud_timeout_ms), vnotify_cloud_delay_ms(_notify_cloud_delay_ms), vnotify_default_delay_ms(_notify_default_delay_ms), vchat_big_size(_chat_big_size), vpush_chat_period_ms(_push_chat_period_ms), vpush_chat_limit(_push_chat_limit), vsaved_gifs_limit(_saved_gifs_limit), vedit_time_limit(_edit_time_limit), vrating_e_decay(_rating_e_decay), vdisabled_features(_disabled_features) {
} }
MTPint vdate; MTPint vdate;
@ -12064,6 +12280,7 @@ public:
MTPint vpush_chat_limit; MTPint vpush_chat_limit;
MTPint vsaved_gifs_limit; MTPint vsaved_gifs_limit;
MTPint vedit_time_limit; MTPint vedit_time_limit;
MTPint vrating_e_decay;
MTPVector<MTPDisabledFeature> vdisabled_features; MTPVector<MTPDisabledFeature> vdisabled_features;
}; };
@ -13212,6 +13429,30 @@ public:
MTPstring vurl; MTPstring vurl;
}; };
class MTPDmessageEntityMentionName : public mtpDataImpl<MTPDmessageEntityMentionName> {
public:
MTPDmessageEntityMentionName() {
}
MTPDmessageEntityMentionName(MTPint _offset, MTPint _length, MTPint _user_id) : voffset(_offset), vlength(_length), vuser_id(_user_id) {
}
MTPint voffset;
MTPint vlength;
MTPint vuser_id;
};
class MTPDinputMessageEntityMentionName : public mtpDataImpl<MTPDinputMessageEntityMentionName> {
public:
MTPDinputMessageEntityMentionName() {
}
MTPDinputMessageEntityMentionName(MTPint _offset, MTPint _length, const MTPInputUser &_user_id) : voffset(_offset), vlength(_length), vuser_id(_user_id) {
}
MTPint voffset;
MTPint vlength;
MTPInputUser vuser_id;
};
class MTPDinputChannel : public mtpDataImpl<MTPDinputChannel> { class MTPDinputChannel : public mtpDataImpl<MTPDinputChannel> {
public: public:
MTPDinputChannel() { MTPDinputChannel() {
@ -14100,6 +14341,55 @@ public:
MTPstring vstart_param; MTPstring vstart_param;
}; };
class MTPDmessages_peerDialogs : public mtpDataImpl<MTPDmessages_peerDialogs> {
public:
MTPDmessages_peerDialogs() {
}
MTPDmessages_peerDialogs(const MTPVector<MTPDialog> &_dialogs, const MTPVector<MTPMessage> &_messages, const MTPVector<MTPChat> &_chats, const MTPVector<MTPUser> &_users, const MTPupdates_State &_state) : vdialogs(_dialogs), vmessages(_messages), vchats(_chats), vusers(_users), vstate(_state) {
}
MTPVector<MTPDialog> vdialogs;
MTPVector<MTPMessage> vmessages;
MTPVector<MTPChat> vchats;
MTPVector<MTPUser> vusers;
MTPupdates_State vstate;
};
class MTPDtopPeer : public mtpDataImpl<MTPDtopPeer> {
public:
MTPDtopPeer() {
}
MTPDtopPeer(const MTPPeer &_peer, const MTPdouble &_rating) : vpeer(_peer), vrating(_rating) {
}
MTPPeer vpeer;
MTPdouble vrating;
};
class MTPDtopPeerCategoryPeers : public mtpDataImpl<MTPDtopPeerCategoryPeers> {
public:
MTPDtopPeerCategoryPeers() {
}
MTPDtopPeerCategoryPeers(const MTPTopPeerCategory &_category, MTPint _count, const MTPVector<MTPTopPeer> &_peers) : vcategory(_category), vcount(_count), vpeers(_peers) {
}
MTPTopPeerCategory vcategory;
MTPint vcount;
MTPVector<MTPTopPeer> vpeers;
};
class MTPDcontacts_topPeers : public mtpDataImpl<MTPDcontacts_topPeers> {
public:
MTPDcontacts_topPeers() {
}
MTPDcontacts_topPeers(const MTPVector<MTPTopPeerCategoryPeers> &_categories, const MTPVector<MTPChat> &_chats, const MTPVector<MTPUser> &_users) : vcategories(_categories), vchats(_chats), vusers(_users) {
}
MTPVector<MTPTopPeerCategoryPeers> vcategories;
MTPVector<MTPChat> vchats;
MTPVector<MTPUser> vusers;
};
// RPC methods // RPC methods
class MTPreq_pq { // RPC method 'req_pq' class MTPreq_pq { // RPC method 'req_pq'
@ -16905,6 +17195,117 @@ public:
} }
}; };
class MTPcontacts_getTopPeers { // RPC method 'contacts.getTopPeers'
public:
enum class Flag : int32 {
f_correspondents = (1 << 0),
f_bots_pm = (1 << 1),
f_bots_inline = (1 << 2),
f_groups = (1 << 10),
f_channels = (1 << 15),
MAX_FIELD = (1 << 15),
};
Q_DECLARE_FLAGS(Flags, Flag);
friend inline Flags operator~(Flag v) { return QFlag(~static_cast<int32>(v)); }
bool is_correspondents() const { return vflags.v & Flag::f_correspondents; }
bool is_bots_pm() const { return vflags.v & Flag::f_bots_pm; }
bool is_bots_inline() const { return vflags.v & Flag::f_bots_inline; }
bool is_groups() const { return vflags.v & Flag::f_groups; }
bool is_channels() const { return vflags.v & Flag::f_channels; }
MTPflags<MTPcontacts_getTopPeers::Flags> vflags;
MTPint voffset;
MTPint vlimit;
MTPint vhash;
MTPcontacts_getTopPeers() {
}
MTPcontacts_getTopPeers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_contacts_getTopPeers) {
read(from, end, cons);
}
MTPcontacts_getTopPeers(const MTPflags<MTPcontacts_getTopPeers::Flags> &_flags, MTPint _offset, MTPint _limit, MTPint _hash) : vflags(_flags), voffset(_offset), vlimit(_limit), vhash(_hash) {
}
uint32 innerLength() const {
return vflags.innerLength() + voffset.innerLength() + vlimit.innerLength() + vhash.innerLength();
}
mtpTypeId type() const {
return mtpc_contacts_getTopPeers;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_contacts_getTopPeers) {
vflags.read(from, end);
voffset.read(from, end);
vlimit.read(from, end);
vhash.read(from, end);
}
void write(mtpBuffer &to) const {
vflags.write(to);
voffset.write(to);
vlimit.write(to);
vhash.write(to);
}
typedef MTPcontacts_TopPeers ResponseType;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(MTPcontacts_getTopPeers::Flags)
class MTPcontacts_GetTopPeers : public MTPBoxed<MTPcontacts_getTopPeers> {
public:
MTPcontacts_GetTopPeers() {
}
MTPcontacts_GetTopPeers(const MTPcontacts_getTopPeers &v) : MTPBoxed<MTPcontacts_getTopPeers>(v) {
}
MTPcontacts_GetTopPeers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPcontacts_getTopPeers>(from, end, cons) {
}
MTPcontacts_GetTopPeers(const MTPflags<MTPcontacts_getTopPeers::Flags> &_flags, MTPint _offset, MTPint _limit, MTPint _hash) : MTPBoxed<MTPcontacts_getTopPeers>(MTPcontacts_getTopPeers(_flags, _offset, _limit, _hash)) {
}
};
class MTPcontacts_resetTopPeerRating { // RPC method 'contacts.resetTopPeerRating'
public:
MTPTopPeerCategory vcategory;
MTPInputPeer vpeer;
MTPcontacts_resetTopPeerRating() {
}
MTPcontacts_resetTopPeerRating(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_contacts_resetTopPeerRating) {
read(from, end, cons);
}
MTPcontacts_resetTopPeerRating(const MTPTopPeerCategory &_category, const MTPInputPeer &_peer) : vcategory(_category), vpeer(_peer) {
}
uint32 innerLength() const {
return vcategory.innerLength() + vpeer.innerLength();
}
mtpTypeId type() const {
return mtpc_contacts_resetTopPeerRating;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_contacts_resetTopPeerRating) {
vcategory.read(from, end);
vpeer.read(from, end);
}
void write(mtpBuffer &to) const {
vcategory.write(to);
vpeer.write(to);
}
typedef MTPBool ResponseType;
};
class MTPcontacts_ResetTopPeerRating : public MTPBoxed<MTPcontacts_resetTopPeerRating> {
public:
MTPcontacts_ResetTopPeerRating() {
}
MTPcontacts_ResetTopPeerRating(const MTPcontacts_resetTopPeerRating &v) : MTPBoxed<MTPcontacts_resetTopPeerRating>(v) {
}
MTPcontacts_ResetTopPeerRating(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPcontacts_resetTopPeerRating>(from, end, cons) {
}
MTPcontacts_ResetTopPeerRating(const MTPTopPeerCategory &_category, const MTPInputPeer &_peer) : MTPBoxed<MTPcontacts_resetTopPeerRating>(MTPcontacts_resetTopPeerRating(_category, _peer)) {
}
};
class MTPmessages_getMessages { // RPC method 'messages.getMessages' class MTPmessages_getMessages { // RPC method 'messages.getMessages'
public: public:
MTPVector<MTPint> vid; MTPVector<MTPint> vid;
@ -19861,6 +20262,45 @@ public:
} }
}; };
class MTPmessages_getPeerDialogs { // RPC method 'messages.getPeerDialogs'
public:
MTPVector<MTPInputPeer> vpeer;
MTPmessages_getPeerDialogs() {
}
MTPmessages_getPeerDialogs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getPeerDialogs) {
read(from, end, cons);
}
MTPmessages_getPeerDialogs(const MTPVector<MTPInputPeer> &_peer) : vpeer(_peer) {
}
uint32 innerLength() const {
return vpeer.innerLength();
}
mtpTypeId type() const {
return mtpc_messages_getPeerDialogs;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getPeerDialogs) {
vpeer.read(from, end);
}
void write(mtpBuffer &to) const {
vpeer.write(to);
}
typedef MTPmessages_PeerDialogs ResponseType;
};
class MTPmessages_GetPeerDialogs : public MTPBoxed<MTPmessages_getPeerDialogs> {
public:
MTPmessages_GetPeerDialogs() {
}
MTPmessages_GetPeerDialogs(const MTPmessages_getPeerDialogs &v) : MTPBoxed<MTPmessages_getPeerDialogs>(v) {
}
MTPmessages_GetPeerDialogs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_getPeerDialogs>(from, end, cons) {
}
MTPmessages_GetPeerDialogs(const MTPVector<MTPInputPeer> &_peer) : MTPBoxed<MTPmessages_getPeerDialogs>(MTPmessages_getPeerDialogs(_peer)) {
}
};
class MTPupdates_getState { // RPC method 'updates.getState' class MTPupdates_getState { // RPC method 'updates.getState'
public: public:
MTPupdates_getState() { MTPupdates_getState() {
@ -22637,8 +23077,8 @@ public:
inline static MTPdcOption new_dcOption(const MTPflags<MTPDdcOption::Flags> &_flags, MTPint _id, const MTPstring &_ip_address, MTPint _port) { inline static MTPdcOption new_dcOption(const MTPflags<MTPDdcOption::Flags> &_flags, MTPint _id, const MTPstring &_ip_address, MTPint _port) {
return MTPdcOption(new MTPDdcOption(_flags, _id, _ip_address, _port)); return MTPdcOption(new MTPDdcOption(_flags, _id, _ip_address, _port));
} }
inline static MTPconfig new_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector<MTPDcOption> &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, MTPint _edit_time_limit, const MTPVector<MTPDisabledFeature> &_disabled_features) { inline static MTPconfig new_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector<MTPDcOption> &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, MTPint _edit_time_limit, MTPint _rating_e_decay, const MTPVector<MTPDisabledFeature> &_disabled_features) {
return MTPconfig(new MTPDconfig(_date, _expires, _test_mode, _this_dc, _dc_options, _chat_size_max, _megagroup_size_max, _forwarded_count_max, _online_update_period_ms, _offline_blur_timeout_ms, _offline_idle_timeout_ms, _online_cloud_timeout_ms, _notify_cloud_delay_ms, _notify_default_delay_ms, _chat_big_size, _push_chat_period_ms, _push_chat_limit, _saved_gifs_limit, _edit_time_limit, _disabled_features)); return MTPconfig(new MTPDconfig(_date, _expires, _test_mode, _this_dc, _dc_options, _chat_size_max, _megagroup_size_max, _forwarded_count_max, _online_update_period_ms, _offline_blur_timeout_ms, _offline_idle_timeout_ms, _online_cloud_timeout_ms, _notify_cloud_delay_ms, _notify_default_delay_ms, _chat_big_size, _push_chat_period_ms, _push_chat_limit, _saved_gifs_limit, _edit_time_limit, _rating_e_decay, _disabled_features));
} }
inline static MTPnearestDc new_nearestDc(const MTPstring &_country, MTPint _this_dc, MTPint _nearest_dc) { inline static MTPnearestDc new_nearestDc(const MTPstring &_country, MTPint _this_dc, MTPint _nearest_dc) {
return MTPnearestDc(new MTPDnearestDc(_country, _this_dc, _nearest_dc)); return MTPnearestDc(new MTPDnearestDc(_country, _this_dc, _nearest_dc));
@ -23009,6 +23449,12 @@ public:
inline static MTPmessageEntity new_messageEntityTextUrl(MTPint _offset, MTPint _length, const MTPstring &_url) { inline static MTPmessageEntity new_messageEntityTextUrl(MTPint _offset, MTPint _length, const MTPstring &_url) {
return MTPmessageEntity(new MTPDmessageEntityTextUrl(_offset, _length, _url)); return MTPmessageEntity(new MTPDmessageEntityTextUrl(_offset, _length, _url));
} }
inline static MTPmessageEntity new_messageEntityMentionName(MTPint _offset, MTPint _length, MTPint _user_id) {
return MTPmessageEntity(new MTPDmessageEntityMentionName(_offset, _length, _user_id));
}
inline static MTPmessageEntity new_inputMessageEntityMentionName(MTPint _offset, MTPint _length, const MTPInputUser &_user_id) {
return MTPmessageEntity(new MTPDinputMessageEntityMentionName(_offset, _length, _user_id));
}
inline static MTPinputChannel new_inputChannelEmpty() { inline static MTPinputChannel new_inputChannelEmpty() {
return MTPinputChannel(mtpc_inputChannelEmpty); return MTPinputChannel(mtpc_inputChannelEmpty);
} }
@ -23192,6 +23638,36 @@ public:
inline static MTPinlineBotSwitchPM new_inlineBotSwitchPM(const MTPstring &_text, const MTPstring &_start_param) { inline static MTPinlineBotSwitchPM new_inlineBotSwitchPM(const MTPstring &_text, const MTPstring &_start_param) {
return MTPinlineBotSwitchPM(new MTPDinlineBotSwitchPM(_text, _start_param)); return MTPinlineBotSwitchPM(new MTPDinlineBotSwitchPM(_text, _start_param));
} }
inline static MTPmessages_peerDialogs new_messages_peerDialogs(const MTPVector<MTPDialog> &_dialogs, const MTPVector<MTPMessage> &_messages, const MTPVector<MTPChat> &_chats, const MTPVector<MTPUser> &_users, const MTPupdates_State &_state) {
return MTPmessages_peerDialogs(new MTPDmessages_peerDialogs(_dialogs, _messages, _chats, _users, _state));
}
inline static MTPtopPeer new_topPeer(const MTPPeer &_peer, const MTPdouble &_rating) {
return MTPtopPeer(new MTPDtopPeer(_peer, _rating));
}
inline static MTPtopPeerCategory new_topPeerCategoryBotsPM() {
return MTPtopPeerCategory(mtpc_topPeerCategoryBotsPM);
}
inline static MTPtopPeerCategory new_topPeerCategoryBotsInline() {
return MTPtopPeerCategory(mtpc_topPeerCategoryBotsInline);
}
inline static MTPtopPeerCategory new_topPeerCategoryCorrespondents() {
return MTPtopPeerCategory(mtpc_topPeerCategoryCorrespondents);
}
inline static MTPtopPeerCategory new_topPeerCategoryGroups() {
return MTPtopPeerCategory(mtpc_topPeerCategoryGroups);
}
inline static MTPtopPeerCategory new_topPeerCategoryChannels() {
return MTPtopPeerCategory(mtpc_topPeerCategoryChannels);
}
inline static MTPtopPeerCategoryPeers new_topPeerCategoryPeers(const MTPTopPeerCategory &_category, MTPint _count, const MTPVector<MTPTopPeer> &_peers) {
return MTPtopPeerCategoryPeers(new MTPDtopPeerCategoryPeers(_category, _count, _peers));
}
inline static MTPcontacts_topPeers new_contacts_topPeersNotModified() {
return MTPcontacts_topPeers(mtpc_contacts_topPeersNotModified);
}
inline static MTPcontacts_topPeers new_contacts_topPeers(const MTPVector<MTPTopPeerCategoryPeers> &_categories, const MTPVector<MTPChat> &_chats, const MTPVector<MTPUser> &_users) {
return MTPcontacts_topPeers(new MTPDcontacts_topPeers(_categories, _chats, _users));
}
}; };
} // namespace internal } // namespace internal
@ -29528,7 +30004,7 @@ inline MTPconfig::MTPconfig() : mtpDataOwner(new MTPDconfig()) {
inline uint32 MTPconfig::innerLength() const { inline uint32 MTPconfig::innerLength() const {
const MTPDconfig &v(c_config()); const MTPDconfig &v(c_config());
return v.vdate.innerLength() + v.vexpires.innerLength() + v.vtest_mode.innerLength() + v.vthis_dc.innerLength() + v.vdc_options.innerLength() + v.vchat_size_max.innerLength() + v.vmegagroup_size_max.innerLength() + v.vforwarded_count_max.innerLength() + v.vonline_update_period_ms.innerLength() + v.voffline_blur_timeout_ms.innerLength() + v.voffline_idle_timeout_ms.innerLength() + v.vonline_cloud_timeout_ms.innerLength() + v.vnotify_cloud_delay_ms.innerLength() + v.vnotify_default_delay_ms.innerLength() + v.vchat_big_size.innerLength() + v.vpush_chat_period_ms.innerLength() + v.vpush_chat_limit.innerLength() + v.vsaved_gifs_limit.innerLength() + v.vedit_time_limit.innerLength() + v.vdisabled_features.innerLength(); return v.vdate.innerLength() + v.vexpires.innerLength() + v.vtest_mode.innerLength() + v.vthis_dc.innerLength() + v.vdc_options.innerLength() + v.vchat_size_max.innerLength() + v.vmegagroup_size_max.innerLength() + v.vforwarded_count_max.innerLength() + v.vonline_update_period_ms.innerLength() + v.voffline_blur_timeout_ms.innerLength() + v.voffline_idle_timeout_ms.innerLength() + v.vonline_cloud_timeout_ms.innerLength() + v.vnotify_cloud_delay_ms.innerLength() + v.vnotify_default_delay_ms.innerLength() + v.vchat_big_size.innerLength() + v.vpush_chat_period_ms.innerLength() + v.vpush_chat_limit.innerLength() + v.vsaved_gifs_limit.innerLength() + v.vedit_time_limit.innerLength() + v.vrating_e_decay.innerLength() + v.vdisabled_features.innerLength();
} }
inline mtpTypeId MTPconfig::type() const { inline mtpTypeId MTPconfig::type() const {
return mtpc_config; return mtpc_config;
@ -29557,6 +30033,7 @@ inline void MTPconfig::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeI
v.vpush_chat_limit.read(from, end); v.vpush_chat_limit.read(from, end);
v.vsaved_gifs_limit.read(from, end); v.vsaved_gifs_limit.read(from, end);
v.vedit_time_limit.read(from, end); v.vedit_time_limit.read(from, end);
v.vrating_e_decay.read(from, end);
v.vdisabled_features.read(from, end); v.vdisabled_features.read(from, end);
} }
inline void MTPconfig::write(mtpBuffer &to) const { inline void MTPconfig::write(mtpBuffer &to) const {
@ -29580,12 +30057,13 @@ inline void MTPconfig::write(mtpBuffer &to) const {
v.vpush_chat_limit.write(to); v.vpush_chat_limit.write(to);
v.vsaved_gifs_limit.write(to); v.vsaved_gifs_limit.write(to);
v.vedit_time_limit.write(to); v.vedit_time_limit.write(to);
v.vrating_e_decay.write(to);
v.vdisabled_features.write(to); v.vdisabled_features.write(to);
} }
inline MTPconfig::MTPconfig(MTPDconfig *_data) : mtpDataOwner(_data) { inline MTPconfig::MTPconfig(MTPDconfig *_data) : mtpDataOwner(_data) {
} }
inline MTPconfig MTP_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector<MTPDcOption> &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, MTPint _edit_time_limit, const MTPVector<MTPDisabledFeature> &_disabled_features) { inline MTPconfig MTP_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector<MTPDcOption> &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, MTPint _edit_time_limit, MTPint _rating_e_decay, const MTPVector<MTPDisabledFeature> &_disabled_features) {
return MTP::internal::TypeCreator::new_config(_date, _expires, _test_mode, _this_dc, _dc_options, _chat_size_max, _megagroup_size_max, _forwarded_count_max, _online_update_period_ms, _offline_blur_timeout_ms, _offline_idle_timeout_ms, _online_cloud_timeout_ms, _notify_cloud_delay_ms, _notify_default_delay_ms, _chat_big_size, _push_chat_period_ms, _push_chat_limit, _saved_gifs_limit, _edit_time_limit, _disabled_features); return MTP::internal::TypeCreator::new_config(_date, _expires, _test_mode, _this_dc, _dc_options, _chat_size_max, _megagroup_size_max, _forwarded_count_max, _online_update_period_ms, _offline_blur_timeout_ms, _offline_idle_timeout_ms, _online_cloud_timeout_ms, _notify_cloud_delay_ms, _notify_default_delay_ms, _chat_big_size, _push_chat_period_ms, _push_chat_limit, _saved_gifs_limit, _edit_time_limit, _rating_e_decay, _disabled_features);
} }
inline MTPnearestDc::MTPnearestDc() : mtpDataOwner(new MTPDnearestDc()) { inline MTPnearestDc::MTPnearestDc() : mtpDataOwner(new MTPDnearestDc()) {
@ -32352,6 +32830,14 @@ inline uint32 MTPmessageEntity::innerLength() const {
const MTPDmessageEntityTextUrl &v(c_messageEntityTextUrl()); const MTPDmessageEntityTextUrl &v(c_messageEntityTextUrl());
return v.voffset.innerLength() + v.vlength.innerLength() + v.vurl.innerLength(); return v.voffset.innerLength() + v.vlength.innerLength() + v.vurl.innerLength();
} }
case mtpc_messageEntityMentionName: {
const MTPDmessageEntityMentionName &v(c_messageEntityMentionName());
return v.voffset.innerLength() + v.vlength.innerLength() + v.vuser_id.innerLength();
}
case mtpc_inputMessageEntityMentionName: {
const MTPDinputMessageEntityMentionName &v(c_inputMessageEntityMentionName());
return v.voffset.innerLength() + v.vlength.innerLength() + v.vuser_id.innerLength();
}
} }
return 0; return 0;
} }
@ -32430,6 +32916,20 @@ inline void MTPmessageEntity::read(const mtpPrime *&from, const mtpPrime *end, m
v.vlength.read(from, end); v.vlength.read(from, end);
v.vurl.read(from, end); v.vurl.read(from, end);
} break; } break;
case mtpc_messageEntityMentionName: _type = cons; {
if (!data) setData(new MTPDmessageEntityMentionName());
MTPDmessageEntityMentionName &v(_messageEntityMentionName());
v.voffset.read(from, end);
v.vlength.read(from, end);
v.vuser_id.read(from, end);
} break;
case mtpc_inputMessageEntityMentionName: _type = cons; {
if (!data) setData(new MTPDinputMessageEntityMentionName());
MTPDinputMessageEntityMentionName &v(_inputMessageEntityMentionName());
v.voffset.read(from, end);
v.vlength.read(from, end);
v.vuser_id.read(from, end);
} break;
default: throw mtpErrorUnexpected(cons, "MTPmessageEntity"); default: throw mtpErrorUnexpected(cons, "MTPmessageEntity");
} }
} }
@ -32492,6 +32992,18 @@ inline void MTPmessageEntity::write(mtpBuffer &to) const {
v.vlength.write(to); v.vlength.write(to);
v.vurl.write(to); v.vurl.write(to);
} break; } break;
case mtpc_messageEntityMentionName: {
const MTPDmessageEntityMentionName &v(c_messageEntityMentionName());
v.voffset.write(to);
v.vlength.write(to);
v.vuser_id.write(to);
} break;
case mtpc_inputMessageEntityMentionName: {
const MTPDinputMessageEntityMentionName &v(c_inputMessageEntityMentionName());
v.voffset.write(to);
v.vlength.write(to);
v.vuser_id.write(to);
} break;
} }
} }
inline MTPmessageEntity::MTPmessageEntity(mtpTypeId type) : mtpDataOwner(0), _type(type) { inline MTPmessageEntity::MTPmessageEntity(mtpTypeId type) : mtpDataOwner(0), _type(type) {
@ -32507,6 +33019,8 @@ inline MTPmessageEntity::MTPmessageEntity(mtpTypeId type) : mtpDataOwner(0), _ty
case mtpc_messageEntityCode: setData(new MTPDmessageEntityCode()); break; case mtpc_messageEntityCode: setData(new MTPDmessageEntityCode()); break;
case mtpc_messageEntityPre: setData(new MTPDmessageEntityPre()); break; case mtpc_messageEntityPre: setData(new MTPDmessageEntityPre()); break;
case mtpc_messageEntityTextUrl: setData(new MTPDmessageEntityTextUrl()); break; case mtpc_messageEntityTextUrl: setData(new MTPDmessageEntityTextUrl()); break;
case mtpc_messageEntityMentionName: setData(new MTPDmessageEntityMentionName()); break;
case mtpc_inputMessageEntityMentionName: setData(new MTPDinputMessageEntityMentionName()); break;
default: throw mtpErrorBadTypeId(type, "MTPmessageEntity"); default: throw mtpErrorBadTypeId(type, "MTPmessageEntity");
} }
} }
@ -32532,6 +33046,10 @@ inline MTPmessageEntity::MTPmessageEntity(MTPDmessageEntityPre *_data) : mtpData
} }
inline MTPmessageEntity::MTPmessageEntity(MTPDmessageEntityTextUrl *_data) : mtpDataOwner(_data), _type(mtpc_messageEntityTextUrl) { inline MTPmessageEntity::MTPmessageEntity(MTPDmessageEntityTextUrl *_data) : mtpDataOwner(_data), _type(mtpc_messageEntityTextUrl) {
} }
inline MTPmessageEntity::MTPmessageEntity(MTPDmessageEntityMentionName *_data) : mtpDataOwner(_data), _type(mtpc_messageEntityMentionName) {
}
inline MTPmessageEntity::MTPmessageEntity(MTPDinputMessageEntityMentionName *_data) : mtpDataOwner(_data), _type(mtpc_inputMessageEntityMentionName) {
}
inline MTPmessageEntity MTP_messageEntityUnknown(MTPint _offset, MTPint _length) { inline MTPmessageEntity MTP_messageEntityUnknown(MTPint _offset, MTPint _length) {
return MTP::internal::TypeCreator::new_messageEntityUnknown(_offset, _length); return MTP::internal::TypeCreator::new_messageEntityUnknown(_offset, _length);
} }
@ -32565,6 +33083,12 @@ inline MTPmessageEntity MTP_messageEntityPre(MTPint _offset, MTPint _length, con
inline MTPmessageEntity MTP_messageEntityTextUrl(MTPint _offset, MTPint _length, const MTPstring &_url) { inline MTPmessageEntity MTP_messageEntityTextUrl(MTPint _offset, MTPint _length, const MTPstring &_url) {
return MTP::internal::TypeCreator::new_messageEntityTextUrl(_offset, _length, _url); return MTP::internal::TypeCreator::new_messageEntityTextUrl(_offset, _length, _url);
} }
inline MTPmessageEntity MTP_messageEntityMentionName(MTPint _offset, MTPint _length, MTPint _user_id) {
return MTP::internal::TypeCreator::new_messageEntityMentionName(_offset, _length, _user_id);
}
inline MTPmessageEntity MTP_inputMessageEntityMentionName(MTPint _offset, MTPint _length, const MTPInputUser &_user_id) {
return MTP::internal::TypeCreator::new_inputMessageEntityMentionName(_offset, _length, _user_id);
}
inline uint32 MTPinputChannel::innerLength() const { inline uint32 MTPinputChannel::innerLength() const {
switch (_type) { switch (_type) {
@ -34263,6 +34787,199 @@ inline MTPinlineBotSwitchPM::MTPinlineBotSwitchPM(MTPDinlineBotSwitchPM *_data)
inline MTPinlineBotSwitchPM MTP_inlineBotSwitchPM(const MTPstring &_text, const MTPstring &_start_param) { inline MTPinlineBotSwitchPM MTP_inlineBotSwitchPM(const MTPstring &_text, const MTPstring &_start_param) {
return MTP::internal::TypeCreator::new_inlineBotSwitchPM(_text, _start_param); return MTP::internal::TypeCreator::new_inlineBotSwitchPM(_text, _start_param);
} }
inline MTPmessages_peerDialogs::MTPmessages_peerDialogs() : mtpDataOwner(new MTPDmessages_peerDialogs()) {
}
inline uint32 MTPmessages_peerDialogs::innerLength() const {
const MTPDmessages_peerDialogs &v(c_messages_peerDialogs());
return v.vdialogs.innerLength() + v.vmessages.innerLength() + v.vchats.innerLength() + v.vusers.innerLength() + v.vstate.innerLength();
}
inline mtpTypeId MTPmessages_peerDialogs::type() const {
return mtpc_messages_peerDialogs;
}
inline void MTPmessages_peerDialogs::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != mtpc_messages_peerDialogs) throw mtpErrorUnexpected(cons, "MTPmessages_peerDialogs");
if (!data) setData(new MTPDmessages_peerDialogs());
MTPDmessages_peerDialogs &v(_messages_peerDialogs());
v.vdialogs.read(from, end);
v.vmessages.read(from, end);
v.vchats.read(from, end);
v.vusers.read(from, end);
v.vstate.read(from, end);
}
inline void MTPmessages_peerDialogs::write(mtpBuffer &to) const {
const MTPDmessages_peerDialogs &v(c_messages_peerDialogs());
v.vdialogs.write(to);
v.vmessages.write(to);
v.vchats.write(to);
v.vusers.write(to);
v.vstate.write(to);
}
inline MTPmessages_peerDialogs::MTPmessages_peerDialogs(MTPDmessages_peerDialogs *_data) : mtpDataOwner(_data) {
}
inline MTPmessages_peerDialogs MTP_messages_peerDialogs(const MTPVector<MTPDialog> &_dialogs, const MTPVector<MTPMessage> &_messages, const MTPVector<MTPChat> &_chats, const MTPVector<MTPUser> &_users, const MTPupdates_State &_state) {
return MTP::internal::TypeCreator::new_messages_peerDialogs(_dialogs, _messages, _chats, _users, _state);
}
inline MTPtopPeer::MTPtopPeer() : mtpDataOwner(new MTPDtopPeer()) {
}
inline uint32 MTPtopPeer::innerLength() const {
const MTPDtopPeer &v(c_topPeer());
return v.vpeer.innerLength() + v.vrating.innerLength();
}
inline mtpTypeId MTPtopPeer::type() const {
return mtpc_topPeer;
}
inline void MTPtopPeer::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != mtpc_topPeer) throw mtpErrorUnexpected(cons, "MTPtopPeer");
if (!data) setData(new MTPDtopPeer());
MTPDtopPeer &v(_topPeer());
v.vpeer.read(from, end);
v.vrating.read(from, end);
}
inline void MTPtopPeer::write(mtpBuffer &to) const {
const MTPDtopPeer &v(c_topPeer());
v.vpeer.write(to);
v.vrating.write(to);
}
inline MTPtopPeer::MTPtopPeer(MTPDtopPeer *_data) : mtpDataOwner(_data) {
}
inline MTPtopPeer MTP_topPeer(const MTPPeer &_peer, const MTPdouble &_rating) {
return MTP::internal::TypeCreator::new_topPeer(_peer, _rating);
}
inline uint32 MTPtopPeerCategory::innerLength() const {
return 0;
}
inline mtpTypeId MTPtopPeerCategory::type() const {
if (!_type) throw mtpErrorUninitialized();
return _type;
}
inline void MTPtopPeerCategory::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
switch (cons) {
case mtpc_topPeerCategoryBotsPM: _type = cons; break;
case mtpc_topPeerCategoryBotsInline: _type = cons; break;
case mtpc_topPeerCategoryCorrespondents: _type = cons; break;
case mtpc_topPeerCategoryGroups: _type = cons; break;
case mtpc_topPeerCategoryChannels: _type = cons; break;
default: throw mtpErrorUnexpected(cons, "MTPtopPeerCategory");
}
}
inline void MTPtopPeerCategory::write(mtpBuffer &to) const {
}
inline MTPtopPeerCategory::MTPtopPeerCategory(mtpTypeId type) : _type(type) {
switch (type) {
case mtpc_topPeerCategoryBotsPM: break;
case mtpc_topPeerCategoryBotsInline: break;
case mtpc_topPeerCategoryCorrespondents: break;
case mtpc_topPeerCategoryGroups: break;
case mtpc_topPeerCategoryChannels: break;
default: throw mtpErrorBadTypeId(type, "MTPtopPeerCategory");
}
}
inline MTPtopPeerCategory MTP_topPeerCategoryBotsPM() {
return MTP::internal::TypeCreator::new_topPeerCategoryBotsPM();
}
inline MTPtopPeerCategory MTP_topPeerCategoryBotsInline() {
return MTP::internal::TypeCreator::new_topPeerCategoryBotsInline();
}
inline MTPtopPeerCategory MTP_topPeerCategoryCorrespondents() {
return MTP::internal::TypeCreator::new_topPeerCategoryCorrespondents();
}
inline MTPtopPeerCategory MTP_topPeerCategoryGroups() {
return MTP::internal::TypeCreator::new_topPeerCategoryGroups();
}
inline MTPtopPeerCategory MTP_topPeerCategoryChannels() {
return MTP::internal::TypeCreator::new_topPeerCategoryChannels();
}
inline MTPtopPeerCategoryPeers::MTPtopPeerCategoryPeers() : mtpDataOwner(new MTPDtopPeerCategoryPeers()) {
}
inline uint32 MTPtopPeerCategoryPeers::innerLength() const {
const MTPDtopPeerCategoryPeers &v(c_topPeerCategoryPeers());
return v.vcategory.innerLength() + v.vcount.innerLength() + v.vpeers.innerLength();
}
inline mtpTypeId MTPtopPeerCategoryPeers::type() const {
return mtpc_topPeerCategoryPeers;
}
inline void MTPtopPeerCategoryPeers::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != mtpc_topPeerCategoryPeers) throw mtpErrorUnexpected(cons, "MTPtopPeerCategoryPeers");
if (!data) setData(new MTPDtopPeerCategoryPeers());
MTPDtopPeerCategoryPeers &v(_topPeerCategoryPeers());
v.vcategory.read(from, end);
v.vcount.read(from, end);
v.vpeers.read(from, end);
}
inline void MTPtopPeerCategoryPeers::write(mtpBuffer &to) const {
const MTPDtopPeerCategoryPeers &v(c_topPeerCategoryPeers());
v.vcategory.write(to);
v.vcount.write(to);
v.vpeers.write(to);
}
inline MTPtopPeerCategoryPeers::MTPtopPeerCategoryPeers(MTPDtopPeerCategoryPeers *_data) : mtpDataOwner(_data) {
}
inline MTPtopPeerCategoryPeers MTP_topPeerCategoryPeers(const MTPTopPeerCategory &_category, MTPint _count, const MTPVector<MTPTopPeer> &_peers) {
return MTP::internal::TypeCreator::new_topPeerCategoryPeers(_category, _count, _peers);
}
inline uint32 MTPcontacts_topPeers::innerLength() const {
switch (_type) {
case mtpc_contacts_topPeers: {
const MTPDcontacts_topPeers &v(c_contacts_topPeers());
return v.vcategories.innerLength() + v.vchats.innerLength() + v.vusers.innerLength();
}
}
return 0;
}
inline mtpTypeId MTPcontacts_topPeers::type() const {
if (!_type) throw mtpErrorUninitialized();
return _type;
}
inline void MTPcontacts_topPeers::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != _type) setData(0);
switch (cons) {
case mtpc_contacts_topPeersNotModified: _type = cons; break;
case mtpc_contacts_topPeers: _type = cons; {
if (!data) setData(new MTPDcontacts_topPeers());
MTPDcontacts_topPeers &v(_contacts_topPeers());
v.vcategories.read(from, end);
v.vchats.read(from, end);
v.vusers.read(from, end);
} break;
default: throw mtpErrorUnexpected(cons, "MTPcontacts_topPeers");
}
}
inline void MTPcontacts_topPeers::write(mtpBuffer &to) const {
switch (_type) {
case mtpc_contacts_topPeers: {
const MTPDcontacts_topPeers &v(c_contacts_topPeers());
v.vcategories.write(to);
v.vchats.write(to);
v.vusers.write(to);
} break;
}
}
inline MTPcontacts_topPeers::MTPcontacts_topPeers(mtpTypeId type) : mtpDataOwner(0), _type(type) {
switch (type) {
case mtpc_contacts_topPeersNotModified: break;
case mtpc_contacts_topPeers: setData(new MTPDcontacts_topPeers()); break;
default: throw mtpErrorBadTypeId(type, "MTPcontacts_topPeers");
}
}
inline MTPcontacts_topPeers::MTPcontacts_topPeers(MTPDcontacts_topPeers *_data) : mtpDataOwner(_data), _type(mtpc_contacts_topPeers) {
}
inline MTPcontacts_topPeers MTP_contacts_topPeersNotModified() {
return MTP::internal::TypeCreator::new_contacts_topPeersNotModified();
}
inline MTPcontacts_topPeers MTP_contacts_topPeers(const MTPVector<MTPTopPeerCategoryPeers> &_categories, const MTPVector<MTPChat> &_chats, const MTPVector<MTPUser> &_users) {
return MTP::internal::TypeCreator::new_contacts_topPeers(_categories, _chats, _users);
}
inline MTPDmessage::Flags mtpCastFlags(MTPDmessageService::Flags flags) { return MTPDmessage::Flags(QFlag(flags)); } inline MTPDmessage::Flags mtpCastFlags(MTPDmessageService::Flags flags) { return MTPDmessage::Flags(QFlag(flags)); }
inline MTPDmessage::Flags mtpCastFlags(MTPflags<MTPDmessageService::Flags> flags) { return mtpCastFlags(flags.v); } inline MTPDmessage::Flags mtpCastFlags(MTPflags<MTPDmessageService::Flags> flags) { return mtpCastFlags(flags.v); }
inline MTPDmessage::Flags mtpCastFlags(MTPDupdateShortMessage::Flags flags) { return MTPDmessage::Flags(QFlag(flags)); } inline MTPDmessage::Flags mtpCastFlags(MTPDupdateShortMessage::Flags flags) { return MTPDmessage::Flags(QFlag(flags)); }

View File

@ -898,31 +898,40 @@ bool Document::updateStatusText() const {
Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) { Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) {
AddComponents(Info::Bit()); AddComponents(Info::Bit());
QString text = _parent->originalText(); const auto textWithEntities = _parent->originalText();
EntitiesInText entities = _parent->originalEntities(); QString mainUrl;
auto text = textWithEntities.text;
auto &entities = textWithEntities.entities;
int32 from = 0, till = text.size(), lnk = entities.size(); int32 from = 0, till = text.size(), lnk = entities.size();
for (int32 i = 0; i < lnk; ++i) { for_const (const auto &entity, entities) {
if (entities[i].type != EntityInTextUrl && entities[i].type != EntityInTextCustomUrl && entities[i].type != EntityInTextEmail) { auto type = entity.type();
if (type != EntityInTextUrl && type != EntityInTextCustomUrl && type != EntityInTextEmail) {
continue; continue;
} }
QString u = entities[i].text, t = text.mid(entities[i].offset, entities[i].length); auto customUrl = entity.data(), entityText = text.mid(entity.offset(), entity.length());
_links.push_back(LinkEntry(u.isEmpty() ? t : u, t)); auto url = customUrl.isEmpty() ? entityText : customUrl;
if (_links.isEmpty()) {
mainUrl = url;
}
_links.push_back(LinkEntry(url, entityText));
} }
while (lnk > 0 && till > from) { while (lnk > 0 && till > from) {
--lnk; --lnk;
if (entities[lnk].type != EntityInTextUrl && entities[lnk].type != EntityInTextCustomUrl && entities[lnk].type != EntityInTextEmail) { const auto &entity = entities.at(lnk);
auto type = entity.type();
if (type != EntityInTextUrl && type != EntityInTextCustomUrl && type != EntityInTextEmail) {
++lnk; ++lnk;
break; break;
} }
int32 afterLinkStart = entities[lnk].offset + entities[lnk].length; int32 afterLinkStart = entity.offset() + entity.length();
if (till > afterLinkStart) { if (till > afterLinkStart) {
if (!QRegularExpression(qsl("^[,.\\s_=+\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$")).match(text.mid(afterLinkStart, till - afterLinkStart)).hasMatch()) { if (!QRegularExpression(qsl("^[,.\\s_=+\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$")).match(text.mid(afterLinkStart, till - afterLinkStart)).hasMatch()) {
++lnk; ++lnk;
break; break;
} }
} }
till = entities[lnk].offset; till = entity.offset();
} }
if (!lnk) { if (!lnk) {
if (QRegularExpression(qsl("^[,.\\s\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$")).match(text.mid(from, till - from)).hasMatch()) { if (QRegularExpression(qsl("^[,.\\s\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$")).match(text.mid(from, till - from)).hasMatch()) {
@ -932,6 +941,7 @@ Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) {
_page = (media && media->type() == MediaTypeWebPage) ? static_cast<HistoryWebPage*>(media)->webpage() : 0; _page = (media && media->type() == MediaTypeWebPage) ? static_cast<HistoryWebPage*>(media)->webpage() : 0;
if (_page) { if (_page) {
mainUrl = _page->url;
if (_page->document) { if (_page->document) {
_photol.reset(new DocumentOpenClickHandler(_page->document)); _photol.reset(new DocumentOpenClickHandler(_page->document));
} else if (_page->photo) { } else if (_page->photo) {
@ -945,8 +955,8 @@ Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) {
} else { } else {
_photol = MakeShared<UrlClickHandler>(_page->url); _photol = MakeShared<UrlClickHandler>(_page->url);
} }
} else if (!_links.isEmpty()) { } else if (!mainUrl.isEmpty()) {
_photol = MakeShared<UrlClickHandler>(_links.front().lnk->text()); _photol = MakeShared<UrlClickHandler>(mainUrl);
} }
if (from >= till && _page) { if (from >= till && _page) {
text = _page->description; text = _page->description;
@ -984,8 +994,7 @@ Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) {
if (_page) { if (_page) {
_title = _page->title; _title = _page->title;
} }
QString url(_page ? _page->url : (_links.isEmpty() ? QString() : _links.at(0).lnk->text())); QVector<QStringRef> parts = mainUrl.splitRef('/');
QVector<QStringRef> parts = url.splitRef('/');
if (!parts.isEmpty()) { if (!parts.isEmpty()) {
QStringRef domain = parts.at(0); QStringRef domain = parts.at(0);
if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others

View File

@ -1260,9 +1260,9 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (_selectedMsgId) repaintItem(_selectedMsgId, -1); if (_selectedMsgId) repaintItem(_selectedMsgId, -1);
} else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) { } else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) {
_menu = new PopupMenu(); _menu = new PopupMenu();
QString copyToClipboardContextItem = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItem() : QString(); QString linkCopyToClipboardText = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItemText() : QString();
if (!copyToClipboardContextItem.isEmpty()) { if (!linkCopyToClipboardText.isEmpty()) {
_menu->addAction(copyToClipboardContextItem, this, SLOT(copyContextUrl()))->setEnabled(true); _menu->addAction(linkCopyToClipboardText, this, SLOT(copyContextUrl()))->setEnabled(true);
} }
_menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true); _menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true);
if (isUponSelected > 1) { if (isUponSelected > 1) {

View File

@ -1030,15 +1030,6 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
if (!data) { if (!data) {
data = _participantsData[cnt] = new ParticipantData(); data = _participantsData[cnt] = new ParticipantData();
data->name.setText(st::profileListNameFont, user->name, _textNameOptions); data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
if (user->botInfo) {
if (user->botInfo->readsAllHistory) {
data->online = lang(lng_status_bot_reads_all);
} else {
data->online = lang(lng_status_bot_not_reads_all);
}
} else {
data->online = App::onlineText(user, l_time);
}
if (_peerChat) { if (_peerChat) {
data->admin = (peerFromUser(_peerChat->creator) == user->id) || (_peerChat->adminsEnabled() && (_peerChat->admins.constFind(user) != _peerChat->admins.cend())); data->admin = (peerFromUser(_peerChat->creator) == user->id) || (_peerChat->adminsEnabled() && (_peerChat->admins.constFind(user) != _peerChat->admins.cend()));
} else if (_peerChannel) { } else if (_peerChannel) {
@ -1046,6 +1037,15 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
} else { } else {
data->admin = false; data->admin = false;
} }
if (user->botInfo) {
if (user->botInfo->readsAllHistory || data->admin) {
data->online = lang(lng_status_bot_reads_all);
} else {
data->online = lang(lng_status_bot_not_reads_all);
}
} else {
data->online = App::onlineText(user, l_time);
}
if (_amCreator) { if (_amCreator) {
data->cankick = (user != App::self()); data->cankick = (user != App::self());
} else if (_peerChat && _peerChat->amAdmin()) { } else if (_peerChat && _peerChat->amAdmin()) {

View File

@ -84,7 +84,13 @@ void MacPrivate::notifyClicked(unsigned long long peer, int msgid) {
void MacPrivate::notifyReplied(unsigned long long peer, int msgid, const char *str) { void MacPrivate::notifyReplied(unsigned long long peer, int msgid, const char *str) {
History *history = App::history(PeerId(peer)); History *history = App::history(PeerId(peer));
App::main()->sendMessage(history, QString::fromUtf8(str), (msgid > 0 && !history->peer->isUser()) ? msgid : 0, false, false); MainWidget::MessageToSend message;
message.history = history;
message.textWithTags = { QString::fromUtf8(str), TextWithTags::Tags() };
message.replyTo = (msgid > 0 && !history->peer->isUser()) ? msgid : 0;
message.broadcast = false;
message.silent = false;
App::main()->sendMessage(message);
} }
PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
@ -425,7 +431,7 @@ void PsMainWindow::psMacUpdateMenu() {
canPaste = !Application::clipboard()->text().isEmpty(); canPaste = !Application::clipboard()->text().isEmpty();
} else if (FlatTextarea *edit = qobject_cast<FlatTextarea*>(focused)) { } else if (FlatTextarea *edit = qobject_cast<FlatTextarea*>(focused)) {
canCut = canCopy = canDelete = edit->textCursor().hasSelection(); canCut = canCopy = canDelete = edit->textCursor().hasSelection();
canSelectAll = !edit->getLastText().isEmpty(); canSelectAll = !edit->isEmpty();
canUndo = edit->isUndoAvailable(); canUndo = edit->isUndoAvailable();
canRedo = edit->isRedoAvailable(); canRedo = edit->isRedoAvailable();
canPaste = !Application::clipboard()->text().isEmpty(); canPaste = !Application::clipboard()->text().isEmpty();

View File

@ -851,7 +851,6 @@ namespace {
_psShadowWindows.setColor(_shInactive); _psShadowWindows.setColor(_shInactive);
} }
if (Global::started()) { if (Global::started()) {
QMetaObject::invokeMethod(App::wnd(), "updateCounter", Qt::QueuedConnection);
App::wnd()->update(); App::wnd()->update();
} }
} return false; } return false;

View File

@ -852,7 +852,6 @@ namespace {
// _psShadowWindows.setColor(_shInactive); // _psShadowWindows.setColor(_shInactive);
//} //}
if (Global::started()) { if (Global::started()) {
QMetaObject::invokeMethod(App::wnd(), "updateCounter", Qt::QueuedConnection);
App::wnd()->update(); App::wnd()->update();
} }
} return false; } return false;

View File

@ -23,10 +23,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Serialize { namespace Serialize {
int stringSize(const QString &str) {
return sizeof(quint32) + str.size() * sizeof(ushort);
}
void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) { void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) {
stream << qint32(loc.width()) << qint32(loc.height()); stream << qint32(loc.width()) << qint32(loc.height());
stream << qint32(loc.dc()) << quint64(loc.volume()) << qint32(loc.local()) << quint64(loc.secret()); stream << qint32(loc.dc()) << quint64(loc.volume()) << qint32(loc.local()) << quint64(loc.secret());

View File

@ -24,7 +24,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Serialize { namespace Serialize {
int stringSize(const QString &str); inline int stringSize(const QString &str) {
return sizeof(quint32) + str.size() * sizeof(ushort);
}
inline int bytearraySize(const QByteArray &arr) {
return sizeof(quint32) + arr.size();
}
inline int dateTimeSize() {
return (sizeof(qint64) + sizeof(quint32) + sizeof(qint8));
}
void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc); void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc);
StorageImageLocation readStorageImageLocation(QDataStream &stream); StorageImageLocation readStorageImageLocation(QDataStream &stream);

View File

@ -1512,3 +1512,18 @@ MsgId clientMsgId() {
Q_ASSERT(currentClientMsgId < EndClientMsgId); Q_ASSERT(currentClientMsgId < EndClientMsgId);
return currentClientMsgId++; return currentClientMsgId++;
} }
QString LocationClickHandler::copyToClipboardContextItemText() const {
return lang(lng_context_copy_link);
}
void LocationClickHandler::onClick(Qt::MouseButton button) const {
if (!psLaunchMaps(_coords)) {
QDesktopServices::openUrl(_text);
}
}
void LocationClickHandler::setup() {
QString latlon(qsl("%1,%2").arg(_coords.lat).arg(_coords.lon));
_text = qsl("https://maps.google.com/maps?q=") + latlon + qsl("&ll=") + latlon + qsl("&z=16");
}

View File

@ -1348,22 +1348,23 @@ public:
LocationClickHandler(const LocationCoords &coords) : _coords(coords) { LocationClickHandler(const LocationCoords &coords) : _coords(coords) {
setup(); setup();
} }
QString copyToClipboardContextItem() const override;
void onClick(Qt::MouseButton button) const override;
QString tooltip() const override {
return QString();
}
QString dragText() const override {
return _text;
}
void copyToClipboard() const override { void copyToClipboard() const override {
if (!_text.isEmpty()) { if (!_text.isEmpty()) {
QApplication::clipboard()->setText(_text); QApplication::clipboard()->setText(_text);
} }
} }
QString copyToClipboardContextItemText() const override;
QString tooltip() const override {
return QString();
}
QString text() const override {
return _text;
}
void onClick(Qt::MouseButton button) const override;
private: private:

View File

@ -83,52 +83,52 @@ inline EmojiPtr emojiFromUrl(const QString &url) {
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e. return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
} }
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int *plen = 0) { inline EmojiPtr emojiFromText(const QChar *ch, const QChar *end, int *outLength = nullptr) {
EmojiPtr emoji = 0; EmojiPtr emoji = nullptr;
if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) { if (ch + 1 < end && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) {
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode(); uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
emoji = emojiGet(code); emoji = emojiGet(code);
if (emoji) { if (emoji) {
if (emoji == TwoSymbolEmoji) { // check two symbol if (emoji == TwoSymbolEmoji) { // check two symbol
if (ch + 3 >= e) { if (ch + 3 >= end) {
emoji = 0; emoji = 0;
} else { } else {
uint32 code2 = ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode())); uint32 code2 = ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode()));
emoji = emojiGet(code, code2); emoji = emojiGet(code, code2);
} }
} else { } else {
if (ch + 2 < e && (ch + 2)->unicode() == 0x200D) { // check sequence if (ch + 2 < end && (ch + 2)->unicode() == 0x200D) { // check sequence
EmojiPtr seq = emojiGet(ch, e); EmojiPtr seq = emojiGet(ch, end);
if (seq) { if (seq) {
emoji = seq; emoji = seq;
} }
} }
} }
} }
} else if (ch + 2 < e && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) { } else if (ch + 2 < end && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) {
uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode(); uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode();
emoji = emojiGet(code); emoji = emojiGet(code);
if (plen) *plen = emoji->len + 1; if (outLength) *outLength = emoji->len + 1;
return emoji; return emoji;
} else if (ch < e) { } else if (ch < end) {
emoji = emojiGet(ch->unicode()); emoji = emojiGet(ch->unicode());
t_assert(emoji != TwoSymbolEmoji); t_assert(emoji != TwoSymbolEmoji);
} }
if (emoji) { if (emoji) {
int32 len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0); int32 len = emoji->len + ((ch + emoji->len < end && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color if (emoji->color && (ch + len + 1 < end && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode())); uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
EmojiPtr col = emojiGet(emoji, color); EmojiPtr col = emojiGet(emoji, color);
if (col && col != emoji) { if (col && col != emoji) {
len += col->len - emoji->len; len += col->len - emoji->len;
emoji = col; emoji = col;
if (ch + len < e && (ch + len)->unicode() == 0xFE0F) { if (ch + len < end && (ch + len)->unicode() == 0xFE0F) {
++len; ++len;
} }
} }
} }
if (plen) *plen = len; if (outLength) *outLength = len;
} }
return emoji; return emoji;
@ -165,26 +165,25 @@ inline bool emojiEdge(const QChar *ch) {
return false; return false;
} }
inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText &entities) { inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) {
if (to > from) { if (to > from) {
for (EntitiesInText::iterator i = entities.begin(), e = entities.end(); i != e; ++i) { for (auto &entity : *inOutEntities) {
if (i->offset >= to - start) break; if (entity.offset() >= to - start) break;
if (i->offset + i->length < from - start) continue; if (entity.offset() + entity.length() < from - start) continue;
if (i->offset >= from - start) { if (entity.offset() >= from - start) {
i->offset -= (from - start - result.size()); entity.extendToLeft(from - start - result.size());
i->length += (from - start - result.size());
} }
if (i->offset + i->length < to - start) { if (entity.offset() + entity.length() < to - start) {
i->length -= (from - start - result.size()); entity.shrinkFromRight(from - start - result.size());
} }
} }
result.append(from, to - from); result.append(from, to - from);
} }
} }
inline QString replaceEmojis(const QString &text, EntitiesInText &entities) { inline QString replaceEmojis(const QString &text, EntitiesInText *inOutEntities) {
QString result; QString result;
int32 currentEntity = 0, entitiesCount = entities.size(); auto currentEntity = inOutEntities->begin(), entitiesEnd = inOutEntities->end();
const QChar *emojiStart = text.constData(), *emojiEnd = emojiStart, *e = text.constData() + text.size(); const QChar *emojiStart = text.constData(), *emojiEnd = emojiStart, *e = text.constData() + text.size();
bool canFindEmoji = true; bool canFindEmoji = true;
for (const QChar *ch = emojiEnd; ch != e;) { for (const QChar *ch = emojiEnd; ch != e;) {
@ -194,18 +193,18 @@ inline QString replaceEmojis(const QString &text, EntitiesInText &entities) {
emojiFind(ch, e, newEmojiEnd, emojiCode); emojiFind(ch, e, newEmojiEnd, emojiCode);
} }
while (currentEntity < entitiesCount && ch >= emojiStart + entities[currentEntity].offset + entities[currentEntity].length) { while (currentEntity != entitiesEnd && ch >= emojiStart + currentEntity->offset() + currentEntity->length()) {
++currentEntity; ++currentEntity;
} }
EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0; EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0;
if (emoji && emoji != TwoSymbolEmoji && if (emoji && emoji != TwoSymbolEmoji &&
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) && (ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
(newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) && (newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
(currentEntity >= entitiesCount || (ch < emojiStart + entities[currentEntity].offset && newEmojiEnd <= emojiStart + entities[currentEntity].offset) || (ch >= emojiStart + entities[currentEntity].offset + entities[currentEntity].length && newEmojiEnd > emojiStart + entities[currentEntity].offset + entities[currentEntity].length)) (currentEntity == entitiesEnd || (ch < emojiStart + currentEntity->offset() && newEmojiEnd <= emojiStart + currentEntity->offset()) || (ch >= emojiStart + currentEntity->offset() + currentEntity->length() && newEmojiEnd > emojiStart + currentEntity->offset() + currentEntity->length()))
) { ) {
if (result.isEmpty()) result.reserve(text.size()); if (result.isEmpty()) result.reserve(text.size());
appendPartToResult(result, emojiStart, emojiEnd, ch, entities); appendPartToResult(result, emojiStart, emojiEnd, ch, inOutEntities);
if (emoji->color) { if (emoji->color) {
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code); EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
@ -233,7 +232,7 @@ inline QString replaceEmojis(const QString &text, EntitiesInText &entities) {
} }
if (result.isEmpty()) return text; if (result.isEmpty()) return text;
appendPartToResult(result, emojiStart, emojiEnd, e, entities); appendPartToResult(result, emojiStart, emojiEnd, e, inOutEntities);
return result; return result;
} }

View File

@ -1011,7 +1011,8 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) {
if (!_inner.document()->pageSize().isNull()) { if (!_inner.document()->pageSize().isNull()) {
_inner.document()->setPageSize(QSizeF(0, 0)); _inner.document()->setPageSize(QSizeF(0, 0));
} }
QTextCursor c(doc->docHandle(), replacePosition); QTextCursor c(doc->docHandle(), 0);
c.setPosition(replacePosition);
c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor); c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor);
if (emoji) { if (emoji) {
insertEmoji(emoji, c); insertEmoji(emoji, c);

View File

@ -23,13 +23,72 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h" #include "mainwindow.h"
FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(parent) QByteArray FlatTextarea::serializeTagsList(const TagList &tags) {
, _oldtext(v) if (tags.isEmpty()) {
return QByteArray();
}
QByteArray tagsSerialized;
{
QBuffer buffer(&tagsSerialized);
buffer.open(QIODevice::WriteOnly);
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
stream << qint32(tags.size());
for_const (auto &tag, tags) {
stream << qint32(tag.offset) << qint32(tag.length) << tag.id;
}
}
return tagsSerialized;
}
FlatTextarea::TagList FlatTextarea::deserializeTagsList(QByteArray data, int textLength) {
TagList result;
if (data.isEmpty()) {
return result;
}
QBuffer buffer(&data);
buffer.open(QIODevice::ReadOnly);
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
qint32 tagCount = 0;
stream >> tagCount;
if (stream.status() != QDataStream::Ok) {
return result;
}
if (tagCount <= 0 || tagCount > textLength) {
return result;
}
for (int i = 0; i < tagCount; ++i) {
qint32 offset = 0, length = 0;
QString id;
stream >> offset >> length >> id;
if (stream.status() != QDataStream::Ok) {
return result;
}
if (offset < 0 || length <= 0 || offset + length > textLength) {
return result;
}
result.push_back({ offset, length, id });
}
return result;
}
QString FlatTextarea::tagsMimeType() {
return qsl("application/x-td-field-tags");
}
FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v, const TagList &tags) : QTextEdit(parent)
, _phVisible(!v.length()) , _phVisible(!v.length())
, a_phLeft(_phVisible ? 0 : st.phShift) , a_phLeft(_phVisible ? 0 : st.phShift)
, a_phAlpha(_phVisible ? 1 : 0) , a_phAlpha(_phVisible ? 1 : 0)
, a_phColor(st.phColor->c) , a_phColor(st.phColor->c)
, _a_appearance(animation(this, &FlatTextarea::step_appearance)) , _a_appearance(animation(this, &FlatTextarea::step_appearance))
, _lastTextWithTags { v, tags }
, _st(st) { , _st(st) {
setAcceptRichText(false); setAcceptRichText(false);
resize(_st.width, _st.font->height); resize(_st.width, _st.font->height);
@ -68,22 +127,41 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const
connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
if (!v.isEmpty()) { if (!_lastTextWithTags.text.isEmpty()) {
setTextFast(v); setTextWithTags(_lastTextWithTags, ClearUndoHistory);
} }
} }
void FlatTextarea::setTextFast(const QString &text, bool clearUndoHistory) { FlatTextarea::TextWithTags FlatTextarea::getTextWithTagsPart(int start, int end) {
if (clearUndoHistory) { TextWithTags result;
setPlainText(text); result.text = getTextPart(start, end, &result.tags);
} else { return result;
QTextCursor c(document()->docHandle(), 0);
c.joinPreviousEditBlock();
c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
c.insertText(text);
c.movePosition(QTextCursor::End);
c.endEditBlock();
} }
void FlatTextarea::setTextWithTags(const TextWithTags &textWithTags, UndoHistoryAction undoHistoryAction) {
_insertedTags = textWithTags.tags;
_insertedTagsAreFromMime = false;
_realInsertPosition = 0;
_realCharsAdded = textWithTags.text.size();
auto doc = document();
auto cursor = QTextCursor(doc->docHandle(), 0);
if (undoHistoryAction == ClearUndoHistory) {
doc->setUndoRedoEnabled(false);
cursor.beginEditBlock();
} else if (undoHistoryAction == MergeWithUndoHistory) {
cursor.joinPreviousEditBlock();
} else {
cursor.beginEditBlock();
}
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
cursor.insertText(textWithTags.text);
cursor.movePosition(QTextCursor::End);
cursor.endEditBlock();
if (undoHistoryAction == ClearUndoHistory) {
doc->setUndoRedoEnabled(true);
}
_insertedTags.clear();
_realInsertPosition = -1;
finishPlaceholder(); finishPlaceholder();
} }
@ -208,7 +286,8 @@ void FlatTextarea::paintEvent(QPaintEvent *e) {
p.setFont(_st.font); p.setFont(_st.font);
p.setPen(a_phColor.current()); p.setPen(a_phColor.current());
if (_st.phAlign == style::al_topleft && _phAfter > 0) { if (_st.phAlign == style::al_topleft && _phAfter > 0) {
p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + _st.font->width(getLastText().mid(0, _phAfter)), _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph); int skipWidth = _st.font->width(getTextWithTags().text.mid(0, _phAfter));
p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + skipWidth, _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph);
} else { } else {
QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom());
p.drawText(phRect, _ph, QTextOption(_st.phAlign)); p.drawText(phRect, _ph, QTextOption(_st.phAlign));
@ -252,14 +331,14 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
return emojiFromUrl(imageName); return emojiFromUrl(imageName);
} }
} }
return 0; return nullptr;
} }
QString FlatTextarea::getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const { QString FlatTextarea::getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const {
t_assert(outInlineBot != nullptr); t_assert(outInlineBot != nullptr);
t_assert(outInlineBotUsername != nullptr); t_assert(outInlineBotUsername != nullptr);
const QString &text(getLastText()); auto &text = getTextWithTags().text;
int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size(); int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size();
if (size > 2 && text.at(0) == '@' && text.at(1).isLetter()) { if (size > 2 && text.at(0) == '@' && text.at(1).isLetter()) {
@ -360,53 +439,72 @@ QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start) const {
return QString(); return QString();
} }
void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) { void FlatTextarea::insertTag(const QString &text, QString tagId) {
QTextCursor c(textCursor()); auto cursor = textCursor();
int32 pos = c.position(); int32 pos = cursor.position();
QTextDocument *doc(document()); auto doc = document();
QTextBlock block = doc->findBlock(pos); auto block = doc->findBlock(pos);
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { for (auto iter = block.begin(); !iter.atEnd(); ++iter) {
QTextFragment fr(iter.fragment()); auto fragment = iter.fragment();
if (!fr.isValid()) continue; t_assert(fragment.isValid());
int32 p = fr.position(), e = (p + fr.length()); int fragmentPosition = fragment.position();
if (p >= pos || e < pos) continue; int fragmentEnd = (fragmentPosition + fragment.length());
if (fragmentPosition >= pos || fragmentEnd < pos) continue;
QTextCharFormat f = fr.charFormat(); auto format = fragment.charFormat();
if (f.isImageFormat()) continue; if (format.isImageFormat()) continue;
bool mentionInCommand = false; bool mentionInCommand = false;
QString t(fr.text()); auto fragmentText = fragment.text();
for (int i = pos - p; i > 0; --i) { for (int i = pos - fragmentPosition; i > 0; --i) {
if (t.at(i - 1) == '@' || t.at(i - 1) == '#' || t.at(i - 1) == '/') { auto previousChar = fragmentText.at(i - 1);
if ((i == pos - p || (t.at(i - 1) == '/' ? t.at(i).isLetterOrNumber() : t.at(i).isLetter()) || t.at(i - 1) == '#') && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { if (previousChar == '@' || previousChar == '#' || previousChar == '/') {
c.setPosition(p + i - 1, QTextCursor::MoveAnchor); if ((i == pos - fragmentPosition || (previousChar == '/' ? fragmentText.at(i).isLetterOrNumber() : fragmentText.at(i).isLetter()) || previousChar == '#') &&
int till = p + i; (i < 2 || !(fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_'))) {
for (; (till < e) && (till - p - i + 1 < str.size()); ++till) { cursor.setPosition(fragmentPosition + i - 1);
if (t.at(till - p).toLower() != str.at(till - p - i + 1).toLower()) { int till = fragmentPosition + i;
for (; (till < fragmentEnd); ++till) {
auto ch = fragmentText.at(till - fragmentPosition);
if (!ch.isLetterOrNumber() && ch != '_') {
break; break;
} }
} }
if (till - p - i + 1 == str.size() && till < e && t.at(till - p) == ' ') { if (till < fragmentEnd && fragmentText.at(till - fragmentPosition) == ' ') {
++till; ++till;
} }
c.setPosition(till, QTextCursor::KeepAnchor); cursor.setPosition(till, QTextCursor::KeepAnchor);
c.insertText(str + ' '); break;
return; } else if ((i == pos - fragmentPosition || fragmentText.at(i).isLetter()) && fragmentText.at(i - 1) == '@' && i > 2 && (fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_') && !mentionInCommand) {
} else if ((i == pos - p || t.at(i).isLetter()) && t.at(i - 1) == '@' && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
mentionInCommand = true; mentionInCommand = true;
--i; --i;
continue; continue;
} }
break; break;
} }
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break; if (pos - fragmentPosition - i > 127 || (!mentionInCommand && (pos - fragmentPosition - i > 63))) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; if (!fragmentText.at(i - 1).isLetterOrNumber() && fragmentText.at(i - 1) != '_') break;
} }
break; break;
} }
c.insertText(str + ' '); if (tagId.isEmpty()) {
QTextCharFormat format = cursor.charFormat();
format.setAnchor(false);
format.setAnchorName(QString());
format.clearForeground();
cursor.insertText(text + ' ', format);
} else {
_insertedTags.clear();
_insertedTags.push_back({ 0, text.size(), tagId });
_insertedTagsAreFromMime = false;
cursor.insertText(text + ' ');
_insertedTags.clear();
}
}
void FlatTextarea::setTagMimeProcessor(std_::unique_ptr<TagMimeProcessor> &&processor) {
_tagMimeProcessor = std_::move(processor);
} }
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const { void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
@ -465,12 +563,79 @@ void FlatTextarea::removeSingleEmoji() {
} }
} }
QString FlatTextarea::getText(int32 start, int32 end) const { namespace {
class TagAccumulator {
public:
TagAccumulator(FlatTextarea::TagList *tags) : _tags(tags) {
}
bool changed() const {
return _changed;
}
void feed(const QString &randomTagId, int currentPosition) {
if (randomTagId == _currentTagId) return;
if (!_currentTagId.isEmpty()) {
int randomPartPosition = _currentTagId.lastIndexOf('/');
t_assert(randomPartPosition > 0);
bool tagChanged = true;
if (_currentTag < _tags->size()) {
auto &alreadyTag = _tags->at(_currentTag);
if (alreadyTag.offset == _currentStart &&
alreadyTag.length == currentPosition - _currentStart &&
alreadyTag.id == _currentTagId.midRef(0, randomPartPosition)) {
tagChanged = false;
}
}
if (tagChanged) {
_changed = true;
FlatTextarea::Tag tag = {
_currentStart,
currentPosition - _currentStart,
_currentTagId.mid(0, randomPartPosition),
};
if (_currentTag < _tags->size()) {
(*_tags)[_currentTag] = tag;
} else {
_tags->push_back(tag);
}
}
++_currentTag;
}
_currentTagId = randomTagId;
_currentStart = currentPosition;
};
void finish() {
if (_currentTag < _tags->size()) {
_tags->resize(_currentTag);
_changed = true;
}
}
private:
FlatTextarea::TagList *_tags;
bool _changed = false;
int _currentTag = 0;
int _currentStart = 0;
QString _currentTagId;
};
} // namespace
QString FlatTextarea::getTextPart(int start, int end, TagList *outTagsList, bool *outTagsChanged) const {
if (end >= 0 && end <= start) return QString(); if (end >= 0 && end <= start) return QString();
if (start < 0) start = 0; if (start < 0) start = 0;
bool full = (start == 0) && (end < 0); bool full = (start == 0) && (end < 0);
TagAccumulator tagAccumulator(outTagsList);
QTextDocument *doc(document()); QTextDocument *doc(document());
QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end); QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end);
if (till.isValid()) till = till.next(); if (till.isValid()) till = till.next();
@ -485,17 +650,28 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
end = possibleLen; end = possibleLen;
} }
for (QTextBlock b = from; b != till; b = b.next()) { bool tillFragmentEnd = full;
for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { for (auto b = from; b != till; b = b.next()) {
for (auto iter = b.begin(); !iter.atEnd(); ++iter) {
QTextFragment fragment(iter.fragment()); QTextFragment fragment(iter.fragment());
if (!fragment.isValid()) continue; if (!fragment.isValid()) continue;
int32 p = full ? 0 : fragment.position(), e = full ? 0 : (p + fragment.length()); int32 p = full ? 0 : fragment.position(), e = full ? 0 : (p + fragment.length());
if (!full) { if (!full) {
if (p >= end || e <= start) { tillFragmentEnd = (e <= end);
if (p == end) {
tagAccumulator.feed(fragment.charFormat().anchorName(), result.size());
}
if (p >= end) {
break;
}
if (e <= start) {
continue; continue;
} }
} }
if (full || p >= start) {
tagAccumulator.feed(fragment.charFormat().anchorName(), result.size());
}
QTextCharFormat f = fragment.charFormat(); QTextCharFormat f = fragment.charFormat();
QString emojiText; QString emojiText;
@ -539,6 +715,13 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
result.append('\n'); result.append('\n');
} }
result.chop(1); result.chop(1);
if (tillFragmentEnd) tagAccumulator.feed(QString(), result.size());
tagAccumulator.finish();
if (outTagsChanged) {
*outTagsChanged = tagAccumulator.changed();
}
return result; return result;
} }
@ -638,7 +821,7 @@ void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp!
continue; continue;
} }
} }
newLinks.push_back(qMakePair(domainOffset - 1, p - start - domainOffset + 2)); newLinks.push_back({ domainOffset - 1, static_cast<int>(p - start - domainOffset + 2) });
offset = matchOffset = p - start; offset = matchOffset = p - start;
} }
@ -652,19 +835,32 @@ QStringList FlatTextarea::linksList() const {
QStringList result; QStringList result;
if (!_links.isEmpty()) { if (!_links.isEmpty()) {
QString text(toPlainText()); QString text(toPlainText());
for (LinkRanges::const_iterator i = _links.cbegin(), e = _links.cend(); i != e; ++i) { for_const (auto &link, _links) {
result.push_back(text.mid(i->first + 1, i->second - 2)); result.push_back(text.mid(link.start + 1, link.length - 2));
} }
} }
return result; return result;
} }
void FlatTextarea::insertFromMimeData(const QMimeData *source) { void FlatTextarea::insertFromMimeData(const QMimeData *source) {
QTextEdit::insertFromMimeData(source); auto mime = tagsMimeType();
if (!_inDrop) emit spacedReturnedPasted(); auto text = source->text();
if (source->hasFormat(mime)) {
auto tagsData = source->data(mime);
_insertedTags = deserializeTagsList(tagsData, text.size());
_insertedTagsAreFromMime = true;
} else {
_insertedTags.clear();
}
auto cursor = textCursor();
_realInsertPosition = qMin(cursor.position(), cursor.anchor());
_realCharsAdded = text.size();
QTextEdit::insertFromMimeData(source);
if (!_inDrop) {
emit spacedReturnedPasted();
_insertedTags.clear();
_realInsertPosition = -1;
} }
void FlatTextarea::correctValue(const QString &was, QString &now) {
} }
void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) { void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
@ -674,7 +870,11 @@ void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
imageFormat.setHeight(eh / cIntRetinaFactor()); imageFormat.setHeight(eh / cIntRetinaFactor());
imageFormat.setName(qsl("emoji://e.") + QString::number(emojiKey(emoji), 16)); imageFormat.setName(qsl("emoji://e.") + QString::number(emojiKey(emoji), 16));
imageFormat.setVerticalAlignment(QTextCharFormat::AlignBaseline); imageFormat.setVerticalAlignment(QTextCharFormat::AlignBaseline);
if (c.charFormat().isAnchor()) {
imageFormat.setAnchor(true);
imageFormat.setAnchorName(c.charFormat().anchorName());
imageFormat.setForeground(st::defaultTextStyle.linkFg);
}
static QString objectReplacement(QChar::ObjectReplacementCharacter); static QString objectReplacement(QChar::ObjectReplacementCharacter);
c.insertText(objectReplacement, imageFormat); c.insertText(objectReplacement, imageFormat);
} }
@ -695,89 +895,240 @@ void FlatTextarea::checkContentHeight() {
} }
} }
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { namespace {
int32 replacePosition = -1, replaceLen = 0;
const EmojiData *emoji = 0;
static QString regular = qsl("Open Sans"), semibold = qsl("Open Sans Semibold"); // Optimization: with null page size document does not re-layout
bool checkTilde = !cRetina() && (font().pixelSize() == 13) && (font().family() == regular), wasTildeFragment = false; // on each insertText / mergeCharFormat.
void prepareFormattingOptimization(QTextDocument *document) {
QTextDocument *doc(document()); if (!document->pageSize().isNull()) {
while (true) { document->setPageSize(QSizeF(0, 0));
int32 start = position, end = position + charsAdded;
QTextBlock from = doc->findBlock(start), till = doc->findBlock(end);
if (till.isValid()) till = till.next();
for (QTextBlock b = from; b != till; b = b.next()) {
for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) {
QTextFragment fragment(iter.fragment());
if (!fragment.isValid()) continue;
int32 fp = fragment.position(), fe = fp + fragment.length();
if (fp >= end || fe <= start) {
continue;
}
if (checkTilde) {
wasTildeFragment = (fragment.charFormat().fontFamily() == semibold);
}
QString t(fragment.text());
const QChar *ch = t.constData(), *e = ch + t.size();
for (; ch != e; ++ch, ++fp) {
int32 emojiLen = 0;
emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
} else {
replacePosition = fp;
replaceLen = emojiLen;
}
break;
}
if (checkTilde && fp >= position) { // tilde fix in OpenSans
bool tilde = (ch->unicode() == '~');
if ((tilde && !wasTildeFragment) || (!tilde && wasTildeFragment)) {
if (replacePosition < 0) {
replacePosition = fp;
replaceLen = 1;
} else {
++replaceLen;
}
} else if (replacePosition >= 0) {
break;
} }
} }
if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) { void removeTags(QTextDocument *document, int from, int end) {
++ch; QTextCursor c(document->docHandle(), 0);
++fp; c.setPosition(from);
} c.setPosition(end, QTextCursor::KeepAnchor);
}
if (replacePosition >= 0) break;
}
if (replacePosition >= 0) break;
}
if (replacePosition >= 0) {
if (!document()->pageSize().isNull()) {
document()->setPageSize(QSizeF(0, 0));
}
QTextCursor c(doc->docHandle(), replacePosition);
c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor);
if (emoji) {
insertEmoji(emoji, c);
} else {
QTextCharFormat format; QTextCharFormat format;
format.setFontFamily(wasTildeFragment ? regular : semibold); format.setAnchor(false);
format.setAnchorName(QString());
format.setForeground(st::black);
c.mergeCharFormat(format); c.mergeCharFormat(format);
} }
charsAdded -= replacePosition + replaceLen - position;
position = replacePosition + (emoji ? 1 : replaceLen);
emoji = 0; // Returns the position of the first inserted tag or "changedEnd" value if none found.
replacePosition = -1; int processInsertedTags(QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) {
int firstTagStart = changedEnd;
int applyNoTagFrom = changedEnd;
for_const (auto &tag, tags) {
int tagFrom = changedPosition + tag.offset;
int tagTo = tagFrom + tag.length;
accumulate_max(tagFrom, changedPosition);
accumulate_min(tagTo, changedEnd);
auto tagId = processor ? processor->tagFromMimeTag(tag.id) : tag.id;
if (tagTo > tagFrom && !tagId.isEmpty()) {
accumulate_min(firstTagStart, tagFrom);
prepareFormattingOptimization(document);
if (applyNoTagFrom < tagFrom) {
removeTags(document, applyNoTagFrom, tagFrom);
}
QTextCursor c(document->docHandle(), 0);
c.setPosition(tagFrom);
c.setPosition(tagTo, QTextCursor::KeepAnchor);
QTextCharFormat format;
format.setAnchor(true);
format.setAnchorName(tagId + '/' + QString::number(rand_value<uint32>()));
format.setForeground(st::defaultTextStyle.linkFg);
c.mergeCharFormat(format);
applyNoTagFrom = tagTo;
}
}
if (applyNoTagFrom < changedEnd) {
removeTags(document, applyNoTagFrom, changedEnd);
}
return firstTagStart;
}
// When inserting a part of text inside a tag we need to have
// a way to know if the insertion replaced the end of the tag
// or it was strictly inside (in the middle) of the tag.
bool wasInsertTillTheEndOfTag(QTextBlock block, QTextBlock::iterator fragmentIt, int insertionEnd) {
auto insertTagName = fragmentIt.fragment().charFormat().anchorName();
while (true) {
for (; !fragmentIt.atEnd(); ++fragmentIt) {
auto fragment = fragmentIt.fragment();
bool fragmentOutsideInsertion = (fragment.position() >= insertionEnd);
if (fragmentOutsideInsertion) {
return (fragment.charFormat().anchorName() != insertTagName);
}
int fragmentEnd = fragment.position() + fragment.length();
bool notFullFragmentInserted = (fragmentEnd > insertionEnd);
if (notFullFragmentInserted) {
return false;
}
}
if (block.isValid()) {
fragmentIt = block.begin();
block = block.next();
} else {
break;
}
}
// Insertion goes till the end of the text => not strictly inside a tag.
return true;
}
struct FormattingAction {
enum class Type {
Invalid,
InsertEmoji,
TildeFont,
RemoveTag,
};
Type type = Type::Invalid;
EmojiPtr emoji = nullptr;
bool isTilde = false;
int intervalStart = 0;
int intervalEnd = 0;
};
} // namespace
void FlatTextarea::processFormatting(int insertPosition, int insertEnd) {
// Tilde formatting.
auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold");
bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont);
bool isTildeFragment = false;
// First tag handling (the one we inserted text to).
bool startTagFound = false;
bool breakTagOnNotLetter = false;
auto doc = document();
// Apply inserted tags.
auto insertedTagsProcessor = _insertedTagsAreFromMime ? _tagMimeProcessor.get() : nullptr;
int breakTagOnNotLetterTill = processInsertedTags(doc, insertPosition, insertEnd,
_insertedTags, insertedTagsProcessor);
using ActionType = FormattingAction::Type;
while (true) {
FormattingAction action;
auto fromBlock = doc->findBlock(insertPosition);
auto tillBlock = doc->findBlock(insertEnd);
if (tillBlock.isValid()) tillBlock = tillBlock.next();
for (auto block = fromBlock; block != tillBlock; block = block.next()) {
for (auto fragmentIt = block.begin(); !fragmentIt.atEnd(); ++fragmentIt) {
auto fragment = fragmentIt.fragment();
t_assert(fragment.isValid());
int fragmentPosition = fragment.position();
if (insertPosition >= fragmentPosition + fragment.length()) {
continue;
}
int changedPositionInFragment = insertPosition - fragmentPosition; // Can be negative.
int changedEndInFragment = insertEnd - fragmentPosition;
if (changedEndInFragment <= 0) {
break;
}
auto charFormat = fragment.charFormat();
if (tildeFormatting) {
isTildeFragment = (charFormat.fontFamily() == semiboldFont);
}
auto fragmentText = fragment.text();
auto *textStart = fragmentText.constData();
auto *textEnd = textStart + fragmentText.size();
if (!startTagFound) {
startTagFound = true;
auto tagName = charFormat.anchorName();
if (!tagName.isEmpty()) {
breakTagOnNotLetter = wasInsertTillTheEndOfTag(block, fragmentIt, insertEnd);
}
}
auto *ch = textStart + qMax(changedPositionInFragment, 0);
for (; ch < textEnd; ++ch) {
int emojiLength = 0;
if (auto emoji = emojiFromText(ch, textEnd, &emojiLength)) {
// Replace emoji if no current action is prepared.
if (action.type == ActionType::Invalid) {
action.type = ActionType::InsertEmoji;
action.emoji = emoji;
action.intervalStart = fragmentPosition + (ch - textStart);
action.intervalEnd = action.intervalStart + emojiLength;
}
break;
}
if (breakTagOnNotLetter && !ch->isLetter()) {
// Remove tag name till the end if no current action is prepared.
if (action.type != ActionType::Invalid) {
break;
}
breakTagOnNotLetter = false;
if (fragmentPosition + (ch - textStart) < breakTagOnNotLetterTill) {
action.type = ActionType::RemoveTag;
action.intervalStart = fragmentPosition + (ch - textStart);
action.intervalEnd = breakTagOnNotLetterTill;
break;
}
}
if (tildeFormatting) { // Tilde symbol fix in OpenSans.
bool tilde = (ch->unicode() == '~');
if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) {
if (action.type == ActionType::Invalid) {
action.type = ActionType::TildeFont;
action.intervalStart = fragmentPosition + (ch - textStart);
action.intervalEnd = action.intervalStart + 1;
action.isTilde = tilde;
} else {
++action.intervalEnd;
}
} else if (action.type == ActionType::TildeFont) {
break;
}
}
if (ch + 1 < textEnd && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) {
++ch;
++fragmentPosition;
}
}
if (action.type != ActionType::Invalid) break;
}
if (action.type != ActionType::Invalid) break;
}
if (action.type != ActionType::Invalid) {
prepareFormattingOptimization(doc);
QTextCursor c(doc->docHandle(), 0);
c.setPosition(action.intervalStart);
c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor);
if (action.type == ActionType::InsertEmoji) {
insertEmoji(action.emoji, c);
insertPosition = action.intervalStart + 1;
} else if (action.type == ActionType::RemoveTag) {
QTextCharFormat format;
format.setAnchor(false);
format.setAnchorName(QString());
format.setForeground(st::black);
c.mergeCharFormat(format);
} else if (action.type == ActionType::TildeFont) {
QTextCharFormat format;
format.setFontFamily(action.isTilde ? semiboldFont : regularFont);
c.mergeCharFormat(format);
insertPosition = action.intervalEnd;
}
} else { } else {
break; break;
} }
@ -787,6 +1138,12 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) { void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) {
if (_correcting) return; if (_correcting) return;
int insertPosition = (_realInsertPosition >= 0) ? _realInsertPosition : position;
int insertLength = (_realInsertPosition >= 0) ? _realCharsAdded : charsAdded;
int removePosition = position;
int removeLength = charsRemoved;
QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock(); QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock();
_correcting = true; _correcting = true;
@ -795,31 +1152,32 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
c.movePosition(QTextCursor::End); c.movePosition(QTextCursor::End);
int32 fullSize = c.position(), toRemove = fullSize - _maxLength; int32 fullSize = c.position(), toRemove = fullSize - _maxLength;
if (toRemove > 0) { if (toRemove > 0) {
if (toRemove > charsAdded) { if (toRemove > insertLength) {
if (charsAdded) { if (insertLength) {
c.setPosition(position); c.setPosition(insertPosition);
c.setPosition((position + charsAdded), QTextCursor::KeepAnchor); c.setPosition((insertPosition + insertLength), QTextCursor::KeepAnchor);
c.removeSelectedText(); c.removeSelectedText();
} }
c.setPosition(fullSize - (toRemove - charsAdded)); c.setPosition(fullSize - (toRemove - insertLength));
c.setPosition(fullSize, QTextCursor::KeepAnchor); c.setPosition(fullSize, QTextCursor::KeepAnchor);
c.removeSelectedText(); c.removeSelectedText();
} else { } else {
c.setPosition(position + (charsAdded - toRemove)); c.setPosition(insertPosition + (insertLength - toRemove));
c.setPosition(position + charsAdded, QTextCursor::KeepAnchor); c.setPosition(insertPosition + insertLength, QTextCursor::KeepAnchor);
c.removeSelectedText(); c.removeSelectedText();
} }
} }
} }
_correcting = false; _correcting = false;
if (insertPosition == removePosition) {
if (!_links.isEmpty()) { if (!_links.isEmpty()) {
bool changed = false; bool changed = false;
for (LinkRanges::iterator i = _links.begin(); i != _links.end();) { for (auto i = _links.begin(); i != _links.end();) {
if (i->first + i->second <= position) { if (i->start + i->length <= insertPosition) {
++i; ++i;
} else if (i->first >= position + charsRemoved) { } else if (i->start >= removePosition + removeLength) {
i->first += charsAdded - charsRemoved; i->start += insertLength - removeLength;
++i; ++i;
} else { } else {
i = _links.erase(i); i = _links.erase(i);
@ -828,30 +1186,25 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
} }
if (changed) emit linksChanged(); if (changed) emit linksChanged();
} }
} else {
parseLinks();
}
if (document()->availableRedoSteps() > 0) { if (document()->availableRedoSteps() > 0) {
QTextCursor(document()->docHandle(), 0).endEditBlock(); QTextCursor(document()->docHandle(), 0).endEditBlock();
return; return;
} }
const int takeBack = 3; if (insertLength <= 0) {
position -= takeBack;
charsAdded += takeBack;
if (position < 0) {
charsAdded += position;
position = 0;
}
if (charsAdded <= 0) {
QTextCursor(document()->docHandle(), 0).endEditBlock(); QTextCursor(document()->docHandle(), 0).endEditBlock();
return; return;
} }
_correcting = true; _correcting = true;
QSizeF s = document()->pageSize(); auto pageSize = document()->pageSize();
processDocumentContentsChange(position, charsAdded); processFormatting(insertPosition, insertPosition + insertLength);
if (document()->pageSize() != s) { if (document()->pageSize() != pageSize) {
document()->setPageSize(s); document()->setPageSize(pageSize);
} }
_correcting = false; _correcting = false;
@ -861,12 +1214,16 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
void FlatTextarea::onDocumentContentsChanged() { void FlatTextarea::onDocumentContentsChanged() {
if (_correcting) return; if (_correcting) return;
QString curText(getText()); auto tagsChanged = false;
auto curText = getTextPart(0, -1, &_lastTextWithTags.tags, &tagsChanged);
_correcting = true; _correcting = true;
correctValue(_oldtext, curText); correctValue(_lastTextWithTags.text, curText, _lastTextWithTags.tags);
_correcting = false; _correcting = false;
if (_oldtext != curText) {
_oldtext = curText; bool textOrTagsChanged = tagsChanged || (_lastTextWithTags.text != curText);
if (textOrTagsChanged) {
_lastTextWithTags.text = curText;
emit changed(); emit changed();
checkContentHeight(); checkContentHeight();
} }
@ -908,12 +1265,16 @@ void FlatTextarea::setPlaceholder(const QString &ph, int32 afterSymbols) {
_phAfter = afterSymbols; _phAfter = afterSymbols;
updatePlaceholder(); updatePlaceholder();
} }
_phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - (_phAfter ? _st.font->width(getLastText().mid(0, _phAfter)) : 0)); int skipWidth = 0;
if (_phAfter) {
skipWidth = _st.font->width(getTextWithTags().text.mid(0, _phAfter));
}
_phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - skipWidth);
if (_phVisible) update(); if (_phVisible) update();
} }
void FlatTextarea::updatePlaceholder() { void FlatTextarea::updatePlaceholder() {
bool vis = (getLastText().size() <= _phAfter); bool vis = (getTextWithTags().text.size() <= _phAfter);
if (vis == _phVisible) return; if (vis == _phVisible) return;
a_phLeft.start(vis ? 0 : _st.phShift); a_phLeft.start(vis ? 0 : _st.phShift);
@ -928,7 +1289,16 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const {
QTextCursor c(textCursor()); QTextCursor c(textCursor());
int32 start = c.selectionStart(), end = c.selectionEnd(); int32 start = c.selectionStart(), end = c.selectionEnd();
if (end > start) { if (end > start) {
result->setText(getText(start, end)); TagList tags;
result->setText(getTextPart(start, end, &tags));
if (!tags.isEmpty()) {
if (_tagMimeProcessor) {
for (auto &tag : tags) {
tag.id = _tagMimeProcessor->mimeTagFromTag(tag.id);
}
}
result->setData(tagsMimeType(), serializeTagsList(tags));
}
} }
return result; return result;
} }
@ -1015,6 +1385,9 @@ void FlatTextarea::dropEvent(QDropEvent *e) {
_inDrop = true; _inDrop = true;
QTextEdit::dropEvent(e); QTextEdit::dropEvent(e);
_inDrop = false; _inDrop = false;
_insertedTags.clear();
_realInsertPosition = -1;
emit spacedReturnedPasted(); emit spacedReturnedPasted();
} }

View File

@ -31,26 +31,27 @@ class FlatTextarea : public QTextEdit {
public: public:
FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &ph = QString(), const QString &val = QString()); struct Tag {
int offset, length;
QString id;
};
using TagList = QVector<Tag>;
struct TextWithTags {
using Tags = FlatTextarea::TagList;
QString text;
Tags tags;
};
bool viewportEvent(QEvent *e) override; static QByteArray serializeTagsList(const TagList &tags);
void touchEvent(QTouchEvent *e); static TagList deserializeTagsList(QByteArray data, int textLength);
void paintEvent(QPaintEvent *e) override; static QString tagsMimeType();
void focusInEvent(QFocusEvent *e) override;
void focusOutEvent(QFocusEvent *e) override; FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &ph = QString(), const QString &val = QString(), const TagList &tags = TagList());
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void dropEvent(QDropEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
void setMaxLength(int32 maxLength); void setMaxLength(int32 maxLength);
void setMinHeight(int32 minHeight); void setMinHeight(int32 minHeight);
void setMaxHeight(int32 maxHeight); void setMaxHeight(int32 maxHeight);
const QString &getLastText() const {
return _oldtext;
}
void setPlaceholder(const QString &ph, int32 afterSymbols = 0); void setPlaceholder(const QString &ph, int32 afterSymbols = 0);
void updatePlaceholder(); void updatePlaceholder();
void finishPlaceholder(); void finishPlaceholder();
@ -92,7 +93,33 @@ public:
}; };
void setSubmitSettings(SubmitSettings settings); void setSubmitSettings(SubmitSettings settings);
void setTextFast(const QString &text, bool clearUndoHistory = true); const TextWithTags &getTextWithTags() const {
return _lastTextWithTags;
}
TextWithTags getTextWithTagsPart(int start, int end = -1);
void insertTag(const QString &text, QString tagId = QString());
bool isEmpty() const {
return _lastTextWithTags.text.isEmpty();
}
enum UndoHistoryAction {
AddToUndoHistory,
MergeWithUndoHistory,
ClearUndoHistory
};
void setTextWithTags(const TextWithTags &textWithTags, UndoHistoryAction undoHistoryAction = AddToUndoHistory);
// If you need to make some preparations of tags before putting them to QMimeData
// (and then to clipboard or to drag-n-drop object), here is a strategy for that.
class TagMimeProcessor {
public:
virtual QString mimeTagFromTag(const QString &tagId) = 0;
virtual QString tagFromMimeTag(const QString &mimeTag) = 0;
virtual ~TagMimeProcessor() {
}
};
void setTagMimeProcessor(std_::unique_ptr<TagMimeProcessor> &&processor);
public slots: public slots:
@ -104,8 +131,6 @@ public slots:
void onUndoAvailable(bool avail); void onUndoAvailable(bool avail);
void onRedoAvailable(bool avail); void onRedoAvailable(bool avail);
void onMentionHashtagOrBotCommandInsert(QString str);
signals: signals:
void resized(); void resized();
@ -118,8 +143,19 @@ signals:
protected: protected:
QString getText(int32 start = 0, int32 end = -1) const; bool viewportEvent(QEvent *e) override;
virtual void correctValue(const QString &was, QString &now); void touchEvent(QTouchEvent *e);
void paintEvent(QPaintEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
void focusOutEvent(QFocusEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void dropEvent(QDropEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
virtual void correctValue(const QString &was, QString &now, TagList &nowTags) {
}
void insertEmoji(EmojiPtr emoji, QTextCursor c); void insertEmoji(EmojiPtr emoji, QTextCursor c);
@ -129,8 +165,21 @@ protected:
private: private:
// "start" and "end" are in coordinates of text where emoji are replaced
// by ObjectReplacementCharacter. If "end" = -1 means get text till the end.
QString getTextPart(int start, int end, TagList *outTagsList, bool *outTagsChanged = nullptr) const;
void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const; void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const;
void processDocumentContentsChange(int position, int charsAdded);
// After any characters added we must postprocess them. This includes:
// 1. Replacing font family to semibold for ~ characters, if we used Open Sans 13px.
// 2. Replacing font family from semibold for all non-~ characters, if we used ...
// 3. Replacing emoji code sequences by ObjectReplacementCharacters with emoji pics.
// 4. Interrupting tags in which the text was inserted by any char except a letter.
// 5. Applying tags from "_insertedTags" in case we pasted text with tags, not just text.
// Rule 4 applies only if we inserted chars not in the middle of a tag (but at the end).
void processFormatting(int changedPosition, int changedEnd);
bool heightAutoupdated(); bool heightAutoupdated();
int _minHeight = -1; // < 0 - no autosize int _minHeight = -1; // < 0 - no autosize
@ -138,7 +187,7 @@ private:
int _maxLength = -1; int _maxLength = -1;
SubmitSettings _submitSettings = SubmitSettings::Enter; SubmitSettings _submitSettings = SubmitSettings::Enter;
QString _ph, _phelided, _oldtext; QString _ph, _phelided;
int _phAfter = 0; int _phAfter = 0;
bool _phVisible; bool _phVisible;
anim::ivalue a_phLeft; anim::ivalue a_phLeft;
@ -146,6 +195,19 @@ private:
anim::cvalue a_phColor; anim::cvalue a_phColor;
Animation _a_appearance; Animation _a_appearance;
TextWithTags _lastTextWithTags;
// Tags list which we should apply while setText() call or insert from mime data.
TagList _insertedTags;
bool _insertedTagsAreFromMime;
// Override insert position and charsAdded from complex text editing
// (like drag-n-drop in the same text edit field).
int _realInsertPosition = -1;
int _realCharsAdded = 0;
std_::unique_ptr<TagMimeProcessor> _tagMimeProcessor;
style::flatTextarea _st; style::flatTextarea _st;
bool _undoAvailable = false; bool _undoAvailable = false;
@ -163,7 +225,33 @@ private:
bool _correcting = false; bool _correcting = false;
typedef QPair<int, int> LinkRange; struct LinkRange {
typedef QList<LinkRange> LinkRanges; int start;
int length;
};
friend bool operator==(const LinkRange &a, const LinkRange &b);
friend bool operator!=(const LinkRange &a, const LinkRange &b);
using LinkRanges = QVector<LinkRange>;
LinkRanges _links; LinkRanges _links;
}; };
inline bool operator==(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) {
return (a.offset == b.offset) && (a.length == b.length) && (a.id == b.id);
}
inline bool operator!=(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) {
return !(a == b);
}
inline bool operator==(const FlatTextarea::TextWithTags &a, const FlatTextarea::TextWithTags &b) {
return (a.text == b.text) && (a.tags == b.tags);
}
inline bool operator!=(const FlatTextarea::TextWithTags &a, const FlatTextarea::TextWithTags &b) {
return !(a == b);
}
inline bool operator==(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) {
return (a.start == b.start) && (a.length == b.length);
}
inline bool operator!=(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) {
return !(a == b);
}

View File

@ -242,27 +242,6 @@ public:
createBlock(); createBlock();
} }
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (!original.isEmpty() && original.at(0) == '/') {
result = original;
fullDisplayed = -4; // bot command
} else if (!original.isEmpty() && original.at(0) == '@') {
result = original;
fullDisplayed = -3; // mention
} else if (!original.isEmpty() && original.at(0) == '#') {
result = original;
fullDisplayed = -2; // hashtag
} else if (reMailStart().match(original).hasMatch()) {
result = original;
fullDisplayed = -1; // email
} else {
QUrl url(original), good(url.isValid() ? url.toEncoded() : "");
QString readable = good.isValid() ? good.toDisplayString() : original;
result = _t->_font->elided(readable, st::linkCropLimit);
fullDisplayed = (result == readable) ? 1 : 0;
}
}
bool checkCommand() { bool checkCommand() {
bool result = false; bool result = false;
for (QChar c = ((ptr < end) ? *ptr : 0); c == TextCommand; c = ((ptr < end) ? *ptr : 0)) { for (QChar c = ((ptr < end) ? *ptr : 0); c == TextCommand; c = ((ptr < end) ? *ptr : 0)) {
@ -274,7 +253,8 @@ public:
return result; return result;
} }
void checkEntities() { // Returns true if at least one entity was parsed in the current position.
bool checkEntities() {
while (!removeFlags.isEmpty() && (ptr >= removeFlags.firstKey() || ptr >= end)) { while (!removeFlags.isEmpty() && (ptr >= removeFlags.firstKey() || ptr >= end)) {
const QList<int32> &removing(removeFlags.first()); const QList<int32> &removing(removeFlags.first());
for (int32 i = removing.size(); i > 0;) { for (int32 i = removing.size(); i > 0;) {
@ -289,49 +269,59 @@ public:
} }
removeFlags.erase(removeFlags.begin()); removeFlags.erase(removeFlags.begin());
} }
while (waitingEntity != entitiesEnd && start + waitingEntity->offset + waitingEntity->length <= ptr) { while (waitingEntity != entitiesEnd && start + waitingEntity->offset() + waitingEntity->length() <= ptr) {
++waitingEntity; ++waitingEntity;
} }
if (waitingEntity == entitiesEnd || ptr < start + waitingEntity->offset) { if (waitingEntity == entitiesEnd || ptr < start + waitingEntity->offset()) {
return; return false;
} }
bool lnk = false;
int32 startFlags = 0; int32 startFlags = 0;
int32 fullDisplayed; QString linkData, linkText;
QString lnkUrl, lnkText; auto type = waitingEntity->type(), linkType = EntityInTextInvalid;
if (waitingEntity->type == EntityInTextCustomUrl) { LinkDisplayStatus linkDisplayStatus = LinkDisplayedFull;
lnk = true; if (type == EntityInTextBold) {
lnkUrl = waitingEntity->text;
lnkText = QString(start + waitingEntity->offset, waitingEntity->length);
fullDisplayed = -5;
} else if (waitingEntity->type == EntityInTextBold) {
startFlags = TextBlockFSemibold; startFlags = TextBlockFSemibold;
} else if (waitingEntity->type == EntityInTextItalic) { } else if (type == EntityInTextItalic) {
startFlags = TextBlockFItalic; startFlags = TextBlockFItalic;
} else if (waitingEntity->type == EntityInTextCode) { } else if (type == EntityInTextCode) {
startFlags = TextBlockFCode; startFlags = TextBlockFCode;
} else if (waitingEntity->type == EntityInTextPre) { } else if (type == EntityInTextPre) {
startFlags = TextBlockFPre; startFlags = TextBlockFPre;
createBlock(); createBlock();
if (!_t->_blocks.isEmpty() && _t->_blocks.back()->type() != TextBlockTNewline) { if (!_t->_blocks.isEmpty() && _t->_blocks.back()->type() != TextBlockTNewline) {
createNewlineBlock(); createNewlineBlock();
} }
} else if (type == EntityInTextUrl
|| type == EntityInTextEmail
|| type == EntityInTextMention
|| type == EntityInTextHashtag
|| type == EntityInTextBotCommand) {
linkType = type;
linkData = QString(start + waitingEntity->offset(), waitingEntity->length());
if (linkType == EntityInTextUrl) {
computeLinkText(linkData, &linkText, &linkDisplayStatus);
} else { } else {
lnk = true; linkText = linkData;
lnkUrl = QString(start + waitingEntity->offset, waitingEntity->length); }
getLinkData(lnkUrl, lnkText, fullDisplayed); } else if (type == EntityInTextCustomUrl || type == EntityInTextMentionName) {
linkType = type;
linkData = waitingEntity->data();
linkText = QString(start + waitingEntity->offset(), waitingEntity->length());
} }
if (lnk) { if (linkType != EntityInTextInvalid) {
createBlock(); createBlock();
links.push_back(TextLinkData(lnkUrl, fullDisplayed)); links.push_back(TextLinkData(linkType, linkText, linkData, linkDisplayStatus));
lnkIndex = 0x8000 + links.size(); lnkIndex = 0x8000 + links.size();
_t->_text += lnkText; for (auto entityEnd = start + waitingEntity->offset() + waitingEntity->length(); ptr < entityEnd; ++ptr) {
ptr = start + waitingEntity->offset + waitingEntity->length; parseCurrentChar();
parseEmojiFromCurrent();
if (sumFinished || _t->_text.size() >= 0x8000) break; // 32k max
}
createBlock(); createBlock();
lnkIndex = 0; lnkIndex = 0;
@ -339,25 +329,27 @@ public:
if (!(flags & startFlags)) { if (!(flags & startFlags)) {
createBlock(); createBlock();
flags |= startFlags; flags |= startFlags;
removeFlags[start + waitingEntity->offset + waitingEntity->length].push_front(startFlags); removeFlags[start + waitingEntity->offset() + waitingEntity->length()].push_front(startFlags);
} }
} }
++waitingEntity; ++waitingEntity;
if (links.size() >= 0x7FFF) { if (links.size() >= 0x7FFF) {
while (waitingEntity != entitiesEnd && ( while (waitingEntity != entitiesEnd && (
waitingEntity->type == EntityInTextUrl || waitingEntity->type() == EntityInTextUrl ||
waitingEntity->type == EntityInTextCustomUrl || waitingEntity->type() == EntityInTextCustomUrl ||
waitingEntity->type == EntityInTextEmail || waitingEntity->type() == EntityInTextEmail ||
waitingEntity->type == EntityInTextHashtag || waitingEntity->type() == EntityInTextHashtag ||
waitingEntity->type == EntityInTextMention || waitingEntity->type() == EntityInTextMention ||
waitingEntity->type == EntityInTextBotCommand || waitingEntity->type() == EntityInTextMentionName ||
waitingEntity->length <= 0)) { waitingEntity->type() == EntityInTextBotCommand ||
waitingEntity->length() <= 0)) {
++waitingEntity; ++waitingEntity;
} }
} else { } else {
while (waitingEntity != entitiesEnd && waitingEntity->length <= 0) ++waitingEntity; while (waitingEntity != entitiesEnd && waitingEntity->length() <= 0) ++waitingEntity;
} }
return true;
} }
bool readSkipBlockCommand() { bool readSkipBlockCommand() {
@ -455,7 +447,7 @@ public:
case TextCommandLinkText: { case TextCommandLinkText: {
createBlock(); createBlock();
int32 len = ptr->unicode(); int32 len = ptr->unicode();
links.push_back(TextLinkData(QString(++ptr, len), false)); links.push_back(TextLinkData(EntityInTextCustomUrl, QString(), QString(++ptr, len), LinkDisplayedFull));
lnkIndex = 0x8000 + links.size(); lnkIndex = 0x8000 + links.size();
} break; } break;
@ -559,18 +551,19 @@ public:
lnkIndex(0), lnkIndex(0),
stopAfterWidth(QFIXED_MAX) { stopAfterWidth(QFIXED_MAX) {
if (options.flags & TextParseLinks) { if (options.flags & TextParseLinks) {
entities = textParseEntities(src, options.flags, rich); textParseEntities(src, options.flags, &entities, rich);
} }
parse(options); parse(options);
} }
TextParser(Text *t, const QString &text, const EntitiesInText &preparsed, const TextParseOptions &options) : _t(t), TextParser(Text *t, const TextWithEntities &textWithEntities, const TextParseOptions &options) : _t(t),
src(text), src(textWithEntities.text),
rich(options.flags & TextParseRichText), rich(options.flags & TextParseRichText),
multiline(options.flags & TextParseMultiline), multiline(options.flags & TextParseMultiline),
maxLnkIndex(0), maxLnkIndex(0),
flags(0), flags(0),
lnkIndex(0), lnkIndex(0),
stopAfterWidth(QFIXED_MAX) { stopAfterWidth(QFIXED_MAX) {
auto preparsed = textWithEntities.entities;
if ((options.flags & TextParseLinks) && !preparsed.isEmpty()) { if ((options.flags & TextParseLinks) && !preparsed.isEmpty()) {
bool parseMentions = (options.flags & TextParseMentions); bool parseMentions = (options.flags & TextParseMentions);
bool parseHashtags = (options.flags & TextParseHashtags); bool parseHashtags = (options.flags & TextParseHashtags);
@ -581,13 +574,13 @@ public:
} else { } else {
int32 i = 0, l = preparsed.size(); int32 i = 0, l = preparsed.size();
entities.reserve(l); entities.reserve(l);
const QChar s = text.size(); const QChar s = src.size();
for (; i < l; ++i) { for (; i < l; ++i) {
EntityInTextType t = preparsed.at(i).type; auto type = preparsed.at(i).type();
if ((t == EntityInTextMention && !parseMentions) || if (((type == EntityInTextMention || type == EntityInTextMentionName) && !parseMentions) ||
(t == EntityInTextHashtag && !parseHashtags) || (type == EntityInTextHashtag && !parseHashtags) ||
(t == EntityInTextBotCommand && !parseBotCommands) || (type == EntityInTextBotCommand && !parseBotCommands) ||
((t == EntityInTextBold || t == EntityInTextItalic || t == EntityInTextCode || t == EntityInTextPre) && !parseMono)) { ((type == EntityInTextBold || type == EntityInTextItalic || type == EntityInTextCode || type == EntityInTextPre) && !parseMono)) {
continue; continue;
} }
entities.push_back(preparsed.at(i)); entities.push_back(preparsed.at(i));
@ -604,8 +597,13 @@ public:
start = src.constData(); start = src.constData();
end = start + src.size(); end = start + src.size();
entitiesEnd = entities.cend();
waitingEntity = entities.cbegin();
while (waitingEntity != entitiesEnd && waitingEntity->length() <= 0) ++waitingEntity;
int firstMonospaceOffset = EntityInText::firstMonospaceOffset(entities, end - start);
ptr = start; ptr = start;
while (ptr != end && chIsTrimmed(*ptr, rich)) { while (ptr != end && chIsTrimmed(*ptr, rich) && ptr != start + firstMonospaceOffset) {
++ptr; ++ptr;
} }
while (ptr != end && chIsTrimmed(*(end - 1), rich)) { while (ptr != end && chIsTrimmed(*(end - 1), rich)) {
@ -624,15 +622,8 @@ public:
ch = emojiLookback = 0; ch = emojiLookback = 0;
lastSkipped = false; lastSkipped = false;
checkTilde = !cRetina() && _t->_font->size() == 13 && _t->_font->flags() == 0; // tilde Open Sans fix checkTilde = !cRetina() && _t->_font->size() == 13 && _t->_font->flags() == 0; // tilde Open Sans fix
entitiesEnd = entities.cend();
waitingEntity = entities.cbegin();
while (waitingEntity != entitiesEnd && waitingEntity->length <= 0) ++waitingEntity;
for (; ptr <= end; ++ptr) { for (; ptr <= end; ++ptr) {
checkEntities(); while (checkEntities() || (rich && checkCommand())) {
if (rich) {
if (checkCommand()) {
checkEntities();
}
} }
parseCurrentChar(); parseCurrentChar();
parseEmojiFromCurrent(); parseEmojiFromCurrent();
@ -656,32 +647,44 @@ public:
lnkIndex = maxLnkIndex + (b->lnkIndex() - 0x8000); lnkIndex = maxLnkIndex + (b->lnkIndex() - 0x8000);
if (_t->_links.size() < lnkIndex) { if (_t->_links.size() < lnkIndex) {
_t->_links.resize(lnkIndex); _t->_links.resize(lnkIndex);
const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]); const TextLinkData &link(links[lnkIndex - maxLnkIndex - 1]);
ClickHandlerPtr lnk; ClickHandlerPtr handler;
if (data.fullDisplayed < -4) { // hidden link switch (link.type) {
lnk.reset(new HiddenUrlClickHandler(data.url)); case EntityInTextCustomUrl: handler.reset(new HiddenUrlClickHandler(link.data)); break;
} else if (data.fullDisplayed < -3) { // bot command case EntityInTextEmail:
lnk.reset(new BotCommandClickHandler(data.url)); case EntityInTextUrl: handler.reset(new UrlClickHandler(link.data, link.displayStatus == LinkDisplayedFull)); break;
} else if (data.fullDisplayed < -2) { // mention case EntityInTextBotCommand: handler.reset(new BotCommandClickHandler(link.data)); break;
case EntityInTextHashtag:
if (options.flags & TextTwitterMentions) { if (options.flags & TextTwitterMentions) {
lnk.reset(new UrlClickHandler(qsl("https://twitter.com/") + data.url.mid(1), true)); handler.reset(new UrlClickHandler(qsl("https://twitter.com/hashtag/") + link.data.mid(1) + qsl("?src=hash"), true));
} else if (options.flags & TextInstagramMentions) { } else if (options.flags & TextInstagramMentions) {
lnk.reset(new UrlClickHandler(qsl("https://instagram.com/") + data.url.mid(1) + '/', true)); handler.reset(new UrlClickHandler(qsl("https://instagram.com/explore/tags/") + link.data.mid(1) + '/', true));
} else { } else {
lnk.reset(new MentionClickHandler(data.url)); handler.reset(new HashtagClickHandler(link.data));
} }
} else if (data.fullDisplayed < -1) { // hashtag break;
case EntityInTextMention:
if (options.flags & TextTwitterMentions) { if (options.flags & TextTwitterMentions) {
lnk.reset(new UrlClickHandler(qsl("https://twitter.com/hashtag/") + data.url.mid(1) + qsl("?src=hash"), true)); handler.reset(new UrlClickHandler(qsl("https://twitter.com/") + link.data.mid(1), true));
} else if (options.flags & TextInstagramMentions) { } else if (options.flags & TextInstagramMentions) {
lnk.reset(new UrlClickHandler(qsl("https://instagram.com/explore/tags/") + data.url.mid(1) + '/', true)); handler.reset(new UrlClickHandler(qsl("https://instagram.com/") + link.data.mid(1) + '/', true));
} else { } else {
lnk.reset(new HashtagClickHandler(data.url)); handler.reset(new MentionClickHandler(link.data));
} }
} else { // email or url break;
lnk.reset(new UrlClickHandler(data.url, data.fullDisplayed != 0)); case EntityInTextMentionName: {
UserId userId = 0;
uint64 accessHash = 0;
if (mentionNameToFields(link.data, &userId, &accessHash)) {
handler.reset(new MentionNameClickHandler(link.text, userId, accessHash));
} else {
LOG(("Bad mention name: %1").arg(link.data));
} }
_t->setLink(lnkIndex, lnk); } break;
}
t_assert(!handler.isNull());
_t->setLink(lnkIndex, handler);
} }
b->setLnkIndex(lnkIndex); b->setLnkIndex(lnkIndex);
} }
@ -693,6 +696,30 @@ public:
private: private:
enum LinkDisplayStatus {
LinkDisplayedFull,
LinkDisplayedElided,
};
struct TextLinkData {
TextLinkData() = default;
TextLinkData(EntityInTextType type, const QString &text, const QString &data, LinkDisplayStatus displayStatus)
: type(type)
, text(text)
, data(data)
, displayStatus(displayStatus) {
}
EntityInTextType type = EntityInTextInvalid;
QString text, data;
LinkDisplayStatus displayStatus = LinkDisplayedFull;
};
void computeLinkText(const QString &linkData, QString *outLinkText, LinkDisplayStatus *outDisplayStatus) {
QUrl url(linkData), good(url.isValid() ? url.toEncoded() : "");
QString readable = good.isValid() ? good.toDisplayString() : linkData;
*outLinkText = _t->_font->elided(readable, st::linkCropLimit);
*outDisplayStatus = (*outLinkText == readable) ? LinkDisplayedFull : LinkDisplayedElided;
}
Text *_t; Text *_t;
QString src; QString src;
const QChar *start, *end, *ptr; const QChar *start, *end, *ptr;
@ -701,12 +728,6 @@ private:
EntitiesInText entities; EntitiesInText entities;
EntitiesInText::const_iterator waitingEntity, entitiesEnd; EntitiesInText::const_iterator waitingEntity, entitiesEnd;
struct TextLinkData {
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
}
QString url;
int32 fullDisplayed; // -5 - custom text link, -4 - bot command, -3 - mention, -2 - hashtag, -1 - email
};
typedef QVector<TextLinkData> TextLinks; typedef QVector<TextLinkData> TextLinks;
TextLinks links; TextLinks links;
@ -2477,7 +2498,7 @@ void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
} }
} }
void Text::setMarkedText(style::font font, const QString &text, const EntitiesInText &entities, const TextParseOptions &options) { void Text::setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options) {
if (!_textStyle) initDefault(); if (!_textStyle) initDefault();
_font = font; _font = font;
clear(); clear();
@ -2512,7 +2533,7 @@ void Text::setMarkedText(style::font font, const QString &text, const EntitiesIn
// newText.append("\n\n").append(text); // newText.append("\n\n").append(text);
// TextParser parser(this, newText, EntitiesInText(), options); // TextParser parser(this, newText, EntitiesInText(), options);
TextParser parser(this, text, entities, options); TextParser parser(this, textWithEntities, options);
} }
recountNaturalSize(true, options.dir); recountNaturalSize(true, options.dir);
} }
@ -2912,148 +2933,133 @@ TextSelection Text::adjustSelection(TextSelection selection, TextSelectType sele
return { from, to }; return { from, to };
} }
QString Text::original(TextSelection selection, ExpandLinksMode mode) const { template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
void Text::enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const {
if (isEmpty() || selection.empty()) { if (isEmpty() || selection.empty()) {
return QString(); return;
} }
QString result, emptyurl; int lnkIndex = 0;
result.reserve(_text.size()); uint16 lnkFrom = 0;
int32 flags = 0;
int32 lnkFrom = 0, lnkIndex = 0; for (auto i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) { int blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex(); uint16 blockFrom = (i == e) ? _text.size() : (*i)->from();
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
if (blockLnkIndex != lnkIndex) {
if (lnkIndex) { // write link
const ClickHandlerPtr &lnk(_links.at(lnkIndex - 1));
const QString &url = (mode == ExpandLinksNone || !lnk) ? emptyurl : lnk->text();
int32 rangeFrom = qMax(int32(selection.from), lnkFrom), rangeTo = qMin(blockFrom, int32(selection.to));
if (rangeTo > rangeFrom) {
QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
if (url.isEmpty() || lnkFrom != rangeFrom || blockFrom != rangeTo) {
result += r;
} else {
QUrl u(url);
QString displayed = (u.isValid() ? u.toDisplayString() : url);
bool shortened = (r.size() > 3) && (_text.midRef(lnkFrom, r.size() - 3) == displayed.midRef(0, r.size() - 3));
bool same = (r == displayed.midRef(0, r.size())) || (r == url.midRef(0, r.size()));
if (same || shortened) {
result += url;
} else if (mode == ExpandLinksAll) {
result.append(r).append(qsl(" ( ")).append(url).append(qsl(" )"));
} else {
result += r;
}
}
}
}
lnkIndex = blockLnkIndex;
lnkFrom = blockFrom;
}
if (i == e) break;
TextBlockType type = (*i)->type();
if (type == TextBlockTSkip) continue;
if (!blockLnkIndex) {
int32 rangeFrom = qMax(selection.from, (*i)->from()), rangeTo = qMin(selection.to, uint16((*i)->from() + TextPainter::_blockLength(this, i, e)));
if (rangeTo > rangeFrom) {
result += _text.midRef(rangeFrom, rangeTo - rangeFrom);
}
}
}
return result;
}
EntitiesInText Text::originalEntities() const {
EntitiesInText result;
QString emptyurl;
int32 originalLength = 0, lnkStart = 0, italicStart = 0, boldStart = 0, codeStart = 0, preStart = 0;
int32 lnkFrom = 0, lnkIndex = 0, flags = 0;
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
int32 blockFlags = (i == e) ? 0 : (*i)->flags(); int32 blockFlags = (i == e) ? 0 : (*i)->flags();
if (blockFlags != flags) {
if ((flags & TextBlockFItalic) && !(blockFlags & TextBlockFItalic)) { // write italic bool checkBlockFlags = (blockFrom >= selection.from) && (blockFrom <= selection.to);
result.push_back(EntityInText(EntityInTextItalic, italicStart, originalLength - italicStart)); if (checkBlockFlags && blockFlags != flags) {
} else if ((blockFlags & TextBlockFItalic) && !(flags & TextBlockFItalic)) { flagsChangeCallback(flags, blockFlags);
italicStart = originalLength;
}
if ((flags & TextBlockFSemibold) && !(blockFlags & TextBlockFSemibold)) {
result.push_back(EntityInText(EntityInTextBold, boldStart, originalLength - boldStart));
} else if ((blockFlags & TextBlockFSemibold) && !(flags & TextBlockFSemibold)) {
boldStart = originalLength;
}
if ((flags & TextBlockFCode) && !(blockFlags & TextBlockFCode)) {
result.push_back(EntityInText(EntityInTextCode, codeStart, originalLength - codeStart));
} else if ((blockFlags & TextBlockFCode) && !(flags & TextBlockFCode)) {
codeStart = originalLength;
}
if ((flags & TextBlockFPre) && !(blockFlags & TextBlockFPre)) {
result.push_back(EntityInText(EntityInTextPre, preStart, originalLength - preStart));
} else if ((blockFlags & TextBlockFPre) && !(flags & TextBlockFPre)) {
preStart = originalLength;
}
flags = blockFlags; flags = blockFlags;
} }
if (blockLnkIndex && !_links.at(blockLnkIndex - 1)) { // ignore empty links
blockLnkIndex = 0;
}
if (blockLnkIndex != lnkIndex) { if (blockLnkIndex != lnkIndex) {
if (lnkIndex) { // write link if (lnkIndex) {
const ClickHandlerPtr &lnk(_links.at(lnkIndex - 1)); auto rangeFrom = qMax(selection.from, lnkFrom);
const QString &url(lnk ? lnk->text() : emptyurl); auto rangeTo = qMin(blockFrom, selection.to);
if (rangeTo > rangeFrom) { // handle click handler
int32 rangeFrom = lnkFrom, rangeTo = blockFrom;
if (rangeTo > rangeFrom) {
QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom); QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
if (url.isEmpty()) { if (lnkFrom != rangeFrom || blockFrom != rangeTo) {
originalLength += r.size(); appendPartCallback(r);
} else { } else {
QUrl u(url); clickHandlerFinishCallback(r, _links.at(lnkIndex - 1));
QString displayed = (u.isValid() ? u.toDisplayString() : url);
bool shortened = (r.size() > 3) && (_text.midRef(lnkFrom, r.size() - 3) == displayed.midRef(0, r.size() - 3));
bool same = (r == displayed.midRef(0, r.size())) || (r == url.midRef(0, r.size()));
if (same || shortened) {
originalLength += url.size();
if (url.at(0) == '@') {
result.push_back(EntityInText(EntityInTextMention, lnkStart, originalLength - lnkStart));
} else if (url.at(0) == '#') {
result.push_back(EntityInText(EntityInTextHashtag, lnkStart, originalLength - lnkStart));
} else if (url.at(0) == '/') {
result.push_back(EntityInText(EntityInTextBotCommand, lnkStart, originalLength - lnkStart));
} else if (url.indexOf('@') > 0 && url.indexOf('/') <= 0) {
result.push_back(EntityInText(EntityInTextEmail, lnkStart, originalLength - lnkStart));
} else {
result.push_back(EntityInText(EntityInTextUrl, lnkStart, originalLength - lnkStart));
}
} else {
originalLength += r.size();
result.push_back(EntityInText(EntityInTextCustomUrl, lnkStart, originalLength - lnkStart, url));
}
} }
} }
} }
lnkIndex = blockLnkIndex; lnkIndex = blockLnkIndex;
if (lnkIndex) { if (lnkIndex) {
lnkFrom = blockFrom; lnkFrom = blockFrom;
lnkStart = originalLength; clickHandlerStartCallback();
} }
} }
if (i == e) break; if (i == e || blockFrom >= selection.to) break;
TextBlockType type = (*i)->type(); if ((*i)->type() == TextBlockTSkip) continue;
if (type == TextBlockTSkip) continue;
if (!blockLnkIndex) { if (!blockLnkIndex) {
int32 rangeFrom = (*i)->from(), rangeTo = uint16((*i)->from() + TextPainter::_blockLength(this, i, e)); auto rangeFrom = qMax(selection.from, blockFrom);
auto rangeTo = qMin(selection.to, uint16(blockFrom + TextPainter::_blockLength(this, i, e)));
if (rangeTo > rangeFrom) { if (rangeTo > rangeFrom) {
originalLength += rangeTo - rangeFrom; appendPartCallback(_text.midRef(rangeFrom, rangeTo - rangeFrom));
} }
} }
} }
}
TextWithEntities Text::originalTextWithEntities(TextSelection selection, ExpandLinksMode mode) const {
TextWithEntities result;
result.text.reserve(_text.size());
int lnkStart = 0, italicStart = 0, boldStart = 0, codeStart = 0, preStart = 0;
auto flagsChangeCallback = [&result, &italicStart, &boldStart, &codeStart, &preStart](int32 oldFlags, int32 newFlags) {
if ((oldFlags & TextBlockFItalic) && !(newFlags & TextBlockFItalic)) { // write italic
result.entities.push_back(EntityInText(EntityInTextItalic, italicStart, result.text.size() - italicStart));
} else if ((newFlags & TextBlockFItalic) && !(oldFlags & TextBlockFItalic)) {
italicStart = result.text.size();
}
if ((oldFlags & TextBlockFSemibold) && !(newFlags & TextBlockFSemibold)) {
result.entities.push_back(EntityInText(EntityInTextBold, boldStart, result.text.size() - boldStart));
} else if ((newFlags & TextBlockFSemibold) && !(oldFlags & TextBlockFSemibold)) {
boldStart = result.text.size();
}
if ((oldFlags & TextBlockFCode) && !(newFlags & TextBlockFCode)) {
result.entities.push_back(EntityInText(EntityInTextCode, codeStart, result.text.size() - codeStart));
} else if ((newFlags & TextBlockFCode) && !(oldFlags & TextBlockFCode)) {
codeStart = result.text.size();
}
if ((oldFlags & TextBlockFPre) && !(newFlags & TextBlockFPre)) {
result.entities.push_back(EntityInText(EntityInTextPre, preStart, result.text.size() - preStart));
} else if ((newFlags & TextBlockFPre) && !(oldFlags & TextBlockFPre)) {
preStart = result.text.size();
}
};
auto clickHandlerStartCallback = [&result, &lnkStart]() {
lnkStart = result.text.size();
};
auto clickHandlerFinishCallback = [mode, &result, &lnkStart](const QStringRef &r, const ClickHandlerPtr &handler) {
auto expanded = handler->getExpandedLinkTextWithEntities(mode, lnkStart, r);
if (expanded.text.isEmpty()) {
result.text += r;
} else {
result.text += expanded.text;
}
if (!expanded.entities.isEmpty()) {
result.entities.append(expanded.entities);
}
};
auto appendPartCallback = [&result](const QStringRef &r) {
result.text += r;
};
enumerateText(selection, appendPartCallback, clickHandlerStartCallback, clickHandlerFinishCallback, flagsChangeCallback);
return result;
}
QString Text::originalText(TextSelection selection, ExpandLinksMode mode) const {
QString result;
result.reserve(_text.size());
auto appendPartCallback = [&result](const QStringRef &r) {
result += r;
};
auto clickHandlerStartCallback = []() {
};
auto clickHandlerFinishCallback = [mode, &result](const QStringRef &r, const ClickHandlerPtr &handler) {
auto expanded = handler->getExpandedLinkText(mode, r);
if (expanded.isEmpty()) {
result += r;
} else {
result += expanded;
}
};
auto flagsChangeCallback = [](int32 oldFlags, int32 newFlags) {
};
enumerateText(selection, appendPartCallback, clickHandlerStartCallback, clickHandlerFinishCallback, flagsChangeCallback);
return result; return result;
} }

View File

@ -97,7 +97,7 @@ public:
int32 countHeight(int32 width) const; int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions); void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap()); void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap());
void setMarkedText(style::font font, const QString &text, const EntitiesInText &entities, const TextParseOptions &options = _defaultOptions); void setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions);
void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk);
bool hasLinks() const; bool hasLinks() const;
@ -178,13 +178,9 @@ public:
int length() const { int length() const {
return _text.size(); return _text.size();
} }
enum ExpandLinksMode {
ExpandLinksNone, TextWithEntities originalTextWithEntities(TextSelection selection = AllTextSelection, ExpandLinksMode mode = ExpandLinksShortened) const;
ExpandLinksShortened, QString originalText(TextSelection selection = AllTextSelection, ExpandLinksMode mode = ExpandLinksShortened) const;
ExpandLinksAll,
};
QString original(TextSelection selection = AllTextSelection, ExpandLinksMode mode = ExpandLinksShortened) const;
EntitiesInText originalEntities() const;
bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation
if (_text.size() < maxdots) return false; if (_text.size() < maxdots) return false;
@ -212,6 +208,10 @@ public:
private: private:
// Template method for originalText(), originalTextWithEntities().
template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
void enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const;
void recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir = Qt::LayoutDirectionAuto); void recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir = Qt::LayoutDirectionAuto);
// clear() deletes all blocks and calls this method // clear() deletes all blocks and calls this method

View File

@ -1211,7 +1211,7 @@ bool textSplit(QString &sendingText, EntitiesInText &sendingEntities, QString &l
int32 s = 0, half = limit / 2, goodLevel = 0; int32 s = 0, half = limit / 2, goodLevel = 0;
for (const QChar *start = leftText.constData(), *ch = start, *end = leftText.constEnd(), *good = ch; ch != end; ++ch, ++s) { for (const QChar *start = leftText.constData(), *ch = start, *end = leftText.constEnd(), *good = ch; ch != end; ++ch, ++s) {
while (currentEntity < entityCount && ch >= start + leftEntities[currentEntity].offset + leftEntities[currentEntity].length) { while (currentEntity < entityCount && ch >= start + leftEntities.at(currentEntity).offset() + leftEntities.at(currentEntity).length()) {
++currentEntity; ++currentEntity;
} }
@ -1225,8 +1225,8 @@ goodCanBreakEntity = canBreakEntity;\
} }
if (s > half) { if (s > half) {
bool inEntity = (currentEntity < entityCount) && (ch > start + leftEntities[currentEntity].offset) && (ch < start + leftEntities[currentEntity].offset + leftEntities[currentEntity].length); bool inEntity = (currentEntity < entityCount) && (ch > start + leftEntities.at(currentEntity).offset()) && (ch < start + leftEntities.at(currentEntity).offset() + leftEntities.at(currentEntity).length());
EntityInTextType entityType = (currentEntity < entityCount) ? leftEntities[currentEntity].type : EntityInTextBold; EntityInTextType entityType = (currentEntity < entityCount) ? leftEntities.at(currentEntity).type() : EntityInTextInvalid;
bool canBreakEntity = (entityType == EntityInTextPre || entityType == EntityInTextCode); bool canBreakEntity = (entityType == EntityInTextPre || entityType == EntityInTextCode);
int32 noEntityLevel = inEntity ? 0 : 1; int32 noEntityLevel = inEntity ? 0 : 1;
if (inEntity && !canBreakEntity) { if (inEntity && !canBreakEntity) {
@ -1241,9 +1241,9 @@ goodCanBreakEntity = canBreakEntity;\
} }
} else if (ch + 1 < end && chIsNewline(*(ch + 1))) { } else if (ch + 1 < end && chIsNewline(*(ch + 1))) {
MARK_GOOD_AS_LEVEL(15); MARK_GOOD_AS_LEVEL(15);
} else if (currentEntity < entityCount && ch + 1 == start + leftEntities[currentEntity].offset && leftEntities[currentEntity].type == EntityInTextPre) { } else if (currentEntity < entityCount && ch + 1 == start + leftEntities.at(currentEntity).offset() && leftEntities.at(currentEntity).type() == EntityInTextPre) {
MARK_GOOD_AS_LEVEL(14); MARK_GOOD_AS_LEVEL(14);
} else if (currentEntity > 0 && ch == start + leftEntities[currentEntity - 1].offset + leftEntities[currentEntity - 1].length && leftEntities[currentEntity - 1].type == EntityInTextPre) { } else if (currentEntity > 0 && ch == start + leftEntities.at(currentEntity - 1).offset() + leftEntities.at(currentEntity - 1).length() && leftEntities.at(currentEntity - 1).type() == EntityInTextPre) {
MARK_GOOD_AS_LEVEL(14); MARK_GOOD_AS_LEVEL(14);
} else { } else {
MARK_GOOD_AS_LEVEL(13); MARK_GOOD_AS_LEVEL(13);
@ -1285,14 +1285,10 @@ goodCanBreakEntity = canBreakEntity;\
if (goodInEntity) { if (goodInEntity) {
if (goodCanBreakEntity) { if (goodCanBreakEntity) {
sendingEntities = leftEntities.mid(0, goodEntity + 1); sendingEntities = leftEntities.mid(0, goodEntity + 1);
sendingEntities.back().length = good - start - sendingEntities.back().offset; sendingEntities.back().updateTextEnd(good - start);
leftEntities = leftEntities.mid(goodEntity); leftEntities = leftEntities.mid(goodEntity);
for (EntitiesInText::iterator i = leftEntities.begin(), e = leftEntities.end(); i != e; ++i) { for (auto &entity : leftEntities) {
i->offset -= good - start; entity.shiftLeft(good - start);
if (i->offset < 0) {
i->length += i->offset;
i->offset = 0;
}
} }
} else { } else {
sendingEntities = leftEntities.mid(0, goodEntity); sendingEntities = leftEntities.mid(0, goodEntity);
@ -1301,8 +1297,8 @@ goodCanBreakEntity = canBreakEntity;\
} else { } else {
sendingEntities = leftEntities.mid(0, goodEntity); sendingEntities = leftEntities.mid(0, goodEntity);
leftEntities = leftEntities.mid(goodEntity); leftEntities = leftEntities.mid(goodEntity);
for (EntitiesInText::iterator i = leftEntities.begin(), e = leftEntities.end(); i != e; ++i) { for (auto &entity : leftEntities) {
i->offset -= good - start; entity.shiftLeft(good - start);
} }
} }
return true; return true;
@ -1367,6 +1363,29 @@ EntitiesInText entitiesFromMTP(const QVector<MTPMessageEntity> &entities) {
case mtpc_messageEntityEmail: { const auto &d(entity.c_messageEntityEmail()); result.push_back(EntityInText(EntityInTextEmail, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityEmail: { const auto &d(entity.c_messageEntityEmail()); result.push_back(EntityInText(EntityInTextEmail, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityHashtag: { const auto &d(entity.c_messageEntityHashtag()); result.push_back(EntityInText(EntityInTextHashtag, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityHashtag: { const auto &d(entity.c_messageEntityHashtag()); result.push_back(EntityInText(EntityInTextHashtag, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityMention: { const auto &d(entity.c_messageEntityMention()); result.push_back(EntityInText(EntityInTextMention, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityMention: { const auto &d(entity.c_messageEntityMention()); result.push_back(EntityInText(EntityInTextMention, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityMentionName: {
const auto &d(entity.c_messageEntityMentionName());
auto data = QString::number(d.vuser_id.v);
if (auto user = App::userLoaded(peerFromUser(d.vuser_id))) {
data += '.' + QString::number(user->access);
}
result.push_back(EntityInText(EntityInTextMentionName, d.voffset.v, d.vlength.v, data));
} break;
case mtpc_inputMessageEntityMentionName: {
const auto &d(entity.c_inputMessageEntityMentionName());
auto data = ([&d]() -> QString {
if (d.vuser_id.type() == mtpc_inputUserSelf) {
return QString::number(MTP::authedId());
} else if (d.vuser_id.type() == mtpc_inputUser) {
const auto &user(d.vuser_id.c_inputUser());
return QString::number(user.vuser_id.v) + '.' + QString::number(user.vaccess_hash.v);
}
return QString();
})();
if (!data.isEmpty()) {
result.push_back(EntityInText(EntityInTextMentionName, d.voffset.v, d.vlength.v, data));
}
} break;
case mtpc_messageEntityBotCommand: { const auto &d(entity.c_messageEntityBotCommand()); result.push_back(EntityInText(EntityInTextBotCommand, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityBotCommand: { const auto &d(entity.c_messageEntityBotCommand()); result.push_back(EntityInText(EntityInTextBotCommand, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityBold: { const auto &d(entity.c_messageEntityBold()); result.push_back(EntityInText(EntityInTextBold, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityBold: { const auto &d(entity.c_messageEntityBold()); result.push_back(EntityInText(EntityInTextBold, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityItalic: { const auto &d(entity.c_messageEntityItalic()); result.push_back(EntityInText(EntityInTextItalic, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityItalic: { const auto &d(entity.c_messageEntityItalic()); result.push_back(EntityInText(EntityInTextItalic, d.voffset.v, d.vlength.v)); } break;
@ -1382,28 +1401,50 @@ MTPVector<MTPMessageEntity> linksToMTP(const EntitiesInText &links, bool sending
MTPVector<MTPMessageEntity> result(MTP_vector<MTPMessageEntity>(0)); MTPVector<MTPMessageEntity> result(MTP_vector<MTPMessageEntity>(0));
auto &v = result._vector().v; auto &v = result._vector().v;
for_const (const auto &link, links) { for_const (const auto &link, links) {
if (link.length <= 0) continue; if (link.length() <= 0) continue;
if (sending && link.type != EntityInTextCode && link.type != EntityInTextPre) continue; if (sending
&& link.type() != EntityInTextCode
&& link.type() != EntityInTextPre
&& link.type() != EntityInTextMentionName) {
continue;
}
auto offset = MTP_int(link.offset), length = MTP_int(link.length); auto offset = MTP_int(link.offset()), length = MTP_int(link.length());
switch (link.type) { switch (link.type()) {
case EntityInTextUrl: v.push_back(MTP_messageEntityUrl(offset, length)); break; case EntityInTextUrl: v.push_back(MTP_messageEntityUrl(offset, length)); break;
case EntityInTextCustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(link.text))); break; case EntityInTextCustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(link.data()))); break;
case EntityInTextEmail: v.push_back(MTP_messageEntityEmail(offset, length)); break; case EntityInTextEmail: v.push_back(MTP_messageEntityEmail(offset, length)); break;
case EntityInTextHashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break; case EntityInTextHashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break;
case EntityInTextMention: v.push_back(MTP_messageEntityMention(offset, length)); break; case EntityInTextMention: v.push_back(MTP_messageEntityMention(offset, length)); break;
case EntityInTextMentionName: {
auto inputUser = ([](const QString &data) -> MTPInputUser {
UserId userId = 0;
uint64 accessHash = 0;
if (mentionNameToFields(data, &userId, &accessHash)) {
if (userId == MTP::authedId()) {
return MTP_inputUserSelf();
}
return MTP_inputUser(MTP_int(userId), MTP_long(accessHash));
}
return MTP_inputUserEmpty();
})(link.data());
if (inputUser.type() != mtpc_inputUserEmpty) {
v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser));
}
} break;
case EntityInTextBotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break; case EntityInTextBotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break;
case EntityInTextBold: v.push_back(MTP_messageEntityBold(offset, length)); break; case EntityInTextBold: v.push_back(MTP_messageEntityBold(offset, length)); break;
case EntityInTextItalic: v.push_back(MTP_messageEntityItalic(offset, length)); break; case EntityInTextItalic: v.push_back(MTP_messageEntityItalic(offset, length)); break;
case EntityInTextCode: v.push_back(MTP_messageEntityCode(offset, length)); break; case EntityInTextCode: v.push_back(MTP_messageEntityCode(offset, length)); break;
case EntityInTextPre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(link.text))); break; case EntityInTextPre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(link.data()))); break;
} }
} }
return result; return result;
} }
EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp! // Some code is duplicated in flattextarea.cpp!
EntitiesInText result, mono; void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich) {
EntitiesInText result;
bool withHashtags = (flags & TextParseHashtags); bool withHashtags = (flags & TextParseHashtags);
bool withMentions = (flags & TextParseMentions); bool withMentions = (flags & TextParseMentions);
@ -1411,6 +1452,9 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
bool withMono = (flags & TextParseMono); bool withMono = (flags & TextParseMono);
if (withMono) { // parse mono entities (code and pre) if (withMono) { // parse mono entities (code and pre)
int existingEntityIndex = 0, existingEntitiesCount = inOutEntities->size();
int existingEntityShiftLeft = 0;
QString newText; QString newText;
int32 offset = 0, matchOffset = offset, len = text.size(), commandOffset = rich ? 0 : len; int32 offset = 0, matchOffset = offset, len = text.size(), commandOffset = rich ? 0 : len;
@ -1434,7 +1478,7 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
auto mCode = _reCode.match(text, matchOffset); auto mCode = _reCode.match(text, matchOffset);
if (!mPre.hasMatch() && !mCode.hasMatch()) break; if (!mPre.hasMatch() && !mCode.hasMatch()) break;
int32 preStart = mPre.hasMatch() ? mPre.capturedStart() : INT_MAX, int preStart = mPre.hasMatch() ? mPre.capturedStart() : INT_MAX,
preEnd = mPre.hasMatch() ? mPre.capturedEnd() : INT_MAX, preEnd = mPre.hasMatch() ? mPre.capturedEnd() : INT_MAX,
codeStart = mCode.hasMatch() ? mCode.capturedStart() : INT_MAX, codeStart = mCode.hasMatch() ? mCode.capturedStart() : INT_MAX,
codeEnd = mCode.hasMatch() ? mCode.capturedEnd() : INT_MAX, codeEnd = mCode.hasMatch() ? mCode.capturedEnd() : INT_MAX,
@ -1472,11 +1516,25 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
continue; continue;
} }
if (newText.isEmpty()) newText.reserve(text.size());
bool addNewlineBefore = false, addNewlineAfter = false; bool addNewlineBefore = false, addNewlineAfter = false;
int32 outerStart = tagStart, outerEnd = tagEnd; int32 outerStart = tagStart, outerEnd = tagEnd;
int32 innerStart = tagStart + mTag.capturedLength(2), innerEnd = tagEnd - mTag.capturedLength(3); int32 innerStart = tagStart + mTag.capturedLength(2), innerEnd = tagEnd - mTag.capturedLength(3);
// Check if start or end sequences intersect any existing entity.
int intersectedEntityEnd = 0;
for_const (auto &entity, *inOutEntities) {
if (qMin(innerStart, entity.offset() + entity.length()) > qMax(outerStart, entity.offset()) ||
qMin(outerEnd, entity.offset() + entity.length()) > qMax(innerEnd, entity.offset())) {
intersectedEntityEnd = entity.offset() + entity.length();
break;
}
}
if (intersectedEntityEnd > 0) {
matchOffset = qMax(innerStart, intersectedEntityEnd);
continue;
}
if (newText.isEmpty()) newText.reserve(text.size());
if (pre) { if (pre) {
while (outerStart > 0 && chIsSpace(*(start + outerStart - 1), rich) && !chIsNewline(*(start + outerStart - 1))) { while (outerStart > 0 && chIsSpace(*(start + outerStart - 1), rich) && !chIsNewline(*(start + outerStart - 1))) {
--outerStart; --outerStart;
@ -1506,14 +1564,27 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
} }
addNewlineAfter = (outerEnd < len && !chIsNewline(*(start + outerEnd))); addNewlineAfter = (outerEnd < len && !chIsNewline(*(start + outerEnd)));
} }
for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() < innerStart; ++existingEntityIndex) {
auto &entity = inOutEntities->at(existingEntityIndex);
result.push_back(entity);
result.back().shiftLeft(existingEntityShiftLeft);
}
if (outerStart > offset) newText.append(start + offset, outerStart - offset); if (outerStart > offset) newText.append(start + offset, outerStart - offset);
if (addNewlineBefore) newText.append('\n'); if (addNewlineBefore) newText.append('\n');
existingEntityShiftLeft += (innerStart - outerStart) - (addNewlineBefore ? 1 : 0);
int32 tagLength = innerEnd - innerStart; int entityStart = newText.size(), entityLength = innerEnd - innerStart;
mono.push_back(EntityInText(pre ? EntityInTextPre : EntityInTextCode, newText.size(), tagLength)); result.push_back(EntityInText(pre ? EntityInTextPre : EntityInTextCode, entityStart, entityLength));
newText.append(start + innerStart, tagLength); for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() <= innerEnd; ++existingEntityIndex) {
auto &entity = inOutEntities->at(existingEntityIndex);
result.push_back(entity);
result.back().shiftLeft(existingEntityShiftLeft);
}
newText.append(start + innerStart, entityLength);
if (addNewlineAfter) newText.append('\n'); if (addNewlineAfter) newText.append('\n');
existingEntityShiftLeft += (outerEnd - innerEnd) - (addNewlineAfter ? 1 : 0);
offset = matchOffset = outerEnd; offset = matchOffset = outerEnd;
} }
@ -1521,8 +1592,19 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
newText.append(start + offset, len - offset); newText.append(start + offset, len - offset);
text = newText; text = newText;
} }
if (!result.isEmpty()) {
for (; existingEntityIndex < existingEntitiesCount; ++existingEntityIndex) {
auto &entity = inOutEntities->at(existingEntityIndex);
result.push_back(entity);
result.back().shiftLeft(existingEntityShiftLeft);
} }
int32 monoEntity = 0, monoCount = mono.size(), monoTill = 0; *inOutEntities = result;
result = EntitiesInText();
}
}
int existingEntityIndex = 0, existingEntitiesCount = inOutEntities->size();
int existingEntityEnd = 0;
initLinkSets(); initLinkSets();
int32 len = text.size(), commandOffset = rich ? 0 : len; int32 len = text.size(), commandOffset = rich ? 0 : len;
@ -1538,11 +1620,11 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
} }
} }
} }
QRegularExpressionMatch mDomain = _reDomain.match(text, matchOffset); auto mDomain = _reDomain.match(text, matchOffset);
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset); auto mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch(); auto mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch(); auto mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
QRegularExpressionMatch mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch(); auto mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch();
EntityInTextType lnkType = EntityInTextUrl; EntityInTextType lnkType = EntityInTextUrl;
int32 lnkStart = 0, lnkLength = 0; int32 lnkStart = 0, lnkLength = 0;
@ -1701,22 +1783,24 @@ EntitiesInText textParseEntities(QString &text, int32 flags, bool rich) { // som
lnkLength = (p - start) - lnkStart; lnkLength = (p - start) - lnkStart;
} }
} }
for (; monoEntity < monoCount && mono[monoEntity].offset <= lnkStart; ++monoEntity) { for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() <= lnkStart; ++existingEntityIndex) {
monoTill = qMax(monoTill, mono[monoEntity].offset + mono[monoEntity].length); auto &entity = inOutEntities->at(existingEntityIndex);
result.push_back(mono[monoEntity]); accumulate_max(existingEntityEnd, entity.offset() + entity.length());
result.push_back(entity);
} }
if (lnkStart >= monoTill) { if (lnkStart >= existingEntityEnd) {
result.push_back(EntityInText(lnkType, lnkStart, lnkLength)); inOutEntities->push_back(EntityInText(lnkType, lnkStart, lnkLength));
} }
offset = matchOffset = lnkStart + lnkLength; offset = matchOffset = lnkStart + lnkLength;
} }
for (; monoEntity < monoCount; ++monoEntity) { if (!result.isEmpty()) {
monoTill = qMax(monoTill, mono[monoEntity].offset + mono[monoEntity].length); for (; existingEntityIndex < existingEntitiesCount; ++existingEntityIndex) {
result.push_back(mono[monoEntity]); auto &entity = inOutEntities->at(existingEntityIndex);
result.push_back(entity);
}
*inOutEntities = result;
} }
return result;
} }
QString textApplyEntities(const QString &text, const EntitiesInText &entities) { QString textApplyEntities(const QString &text, const EntitiesInText &entities) {
@ -1728,15 +1812,15 @@ QString textApplyEntities(const QString &text, const EntitiesInText &entities) {
QString result; QString result;
int32 size = text.size(); int32 size = text.size();
const QChar *b = text.constData(), *already = b, *e = b + size; const QChar *b = text.constData(), *already = b, *e = b + size;
EntitiesInText::const_iterator entity = entities.cbegin(), end = entities.cend(); auto entity = entities.cbegin(), end = entities.cend();
while (entity != end && ((entity->type != EntityInTextCode && entity->type != EntityInTextPre) || entity->length <= 0 || entity->offset >= size)) { while (entity != end && ((entity->type() != EntityInTextCode && entity->type() != EntityInTextPre) || entity->length() <= 0 || entity->offset() >= size)) {
++entity; ++entity;
} }
while (entity != end || !closingTags.isEmpty()) { while (entity != end || !closingTags.isEmpty()) {
int32 nextOpenEntity = (entity == end) ? (size + 1) : entity->offset; int32 nextOpenEntity = (entity == end) ? (size + 1) : entity->offset();
int32 nextCloseEntity = closingTags.isEmpty() ? (size + 1) : closingTags.cbegin().key(); int32 nextCloseEntity = closingTags.isEmpty() ? (size + 1) : closingTags.cbegin().key();
if (nextOpenEntity <= nextCloseEntity) { if (nextOpenEntity <= nextCloseEntity) {
QString tag = (entity->type == EntityInTextCode) ? code : pre; QString tag = (entity->type() == EntityInTextCode) ? code : pre;
if (result.isEmpty()) result.reserve(text.size() + entities.size() * pre.size() * 2); if (result.isEmpty()) result.reserve(text.size() + entities.size() * pre.size() * 2);
const QChar *offset = b + nextOpenEntity; const QChar *offset = b + nextOpenEntity;
@ -1745,10 +1829,10 @@ QString textApplyEntities(const QString &text, const EntitiesInText &entities) {
already = offset; already = offset;
} }
result.append(tag); result.append(tag);
closingTags.insert(qMin(entity->offset + entity->length, size), tag); closingTags.insert(qMin(entity->offset() + entity->length(), size), tag);
++entity; ++entity;
while (entity != end && ((entity->type != EntityInTextCode && entity->type != EntityInTextPre) || entity->length <= 0 || entity->offset >= size)) { while (entity != end && ((entity->type() != EntityInTextCode && entity->type() != EntityInTextPre) || entity->length() <= 0 || entity->offset() >= size)) {
++entity; ++entity;
} }
} else { } else {
@ -1771,79 +1855,18 @@ QString textApplyEntities(const QString &text, const EntitiesInText &entities) {
return result; return result;
} }
void replaceStringWithEntities(const QLatin1String &from, QChar to, QString &result, EntitiesInText &entities, bool checkSpace = false) { void moveStringPart(QChar *start, int32 &to, int32 &from, int32 count, EntitiesInText *inOutEntities) {
int32 len = from.size(), s = result.size(), offset = 0, length = 0;
EntitiesInText::iterator i = entities.begin(), e = entities.end();
for (QChar *start = result.data(); offset < s;) {
int32 nextOffset = result.indexOf(from, offset);
if (nextOffset < 0) {
moveStringPart(start, length, offset, s - offset, entities);
break;
}
if (checkSpace) {
bool spaceBefore = (nextOffset > 0) && (start + nextOffset - 1)->isSpace();
bool spaceAfter = (nextOffset + len < s) && (start + nextOffset + len)->isSpace();
if (!spaceBefore && !spaceAfter) {
moveStringPart(start, length, offset, nextOffset - offset + len + 1, entities);
continue;
}
}
bool skip = false;
for (; i != e; ++i) { // find and check next finishing entity
if (i->offset + i->length > nextOffset) {
skip = (i->offset < nextOffset + len);
break;
}
}
if (skip) {
moveStringPart(start, length, offset, nextOffset - offset + len, entities);
continue;
}
moveStringPart(start, length, offset, nextOffset - offset, entities);
*(start + length) = to;
++length;
offset += len;
}
if (length < s) result.resize(length);
}
QString prepareTextWithEntities(QString result, EntitiesInText &entities, int32 flags) {
cleanTextWithEntities(result, entities);
if (flags) {
entities = textParseEntities(result, flags);
}
replaceStringWithEntities(qstr("--"), QChar(8212), result, entities, true);
replaceStringWithEntities(qstr("<<"), QChar(171), result, entities);
replaceStringWithEntities(qstr(">>"), QChar(187), result, entities);
if (cReplaceEmojis()) {
result = replaceEmojis(result, entities);
}
trimTextWithEntities(result, entities);
return result;
}
void moveStringPart(QChar *start, int32 &to, int32 &from, int32 count, EntitiesInText &entities) {
if (count > 0) { if (count > 0) {
if (to < from) { if (to < from) {
memmove(start + to, start + from, count * sizeof(QChar)); memmove(start + to, start + from, count * sizeof(QChar));
for (auto &entity : entities) { for (auto &entity : *inOutEntities) {
if (entity.offset >= from + count) break; if (entity.offset() >= from + count) break;
if (entity.offset + entity.length < from) continue; if (entity.offset() + entity.length() < from) continue;
if (entity.offset >= from) { if (entity.offset() >= from) {
entity.offset -= (from - to); entity.extendToLeft(from - to);
entity.length += (from - to);
} }
if (entity.offset + entity.length < from + count) { if (entity.offset() + entity.length() < from + count) {
entity.length -= (from - to); entity.shrinkFromRight(from - to);
} }
} }
} }
@ -1852,64 +1875,116 @@ void moveStringPart(QChar *start, int32 &to, int32 &from, int32 count, EntitiesI
} }
} }
void replaceStringWithEntities(const QLatin1String &from, QChar to, QString &result, EntitiesInText *inOutEntities, bool checkSpace = false) {
int32 len = from.size(), s = result.size(), offset = 0, length = 0;
EntitiesInText::iterator i = inOutEntities->begin(), e = inOutEntities->end();
for (QChar *start = result.data(); offset < s;) {
int32 nextOffset = result.indexOf(from, offset);
if (nextOffset < 0) {
moveStringPart(start, length, offset, s - offset, inOutEntities);
break;
}
if (checkSpace) {
bool spaceBefore = (nextOffset > 0) && (start + nextOffset - 1)->isSpace();
bool spaceAfter = (nextOffset + len < s) && (start + nextOffset + len)->isSpace();
if (!spaceBefore && !spaceAfter) {
moveStringPart(start, length, offset, nextOffset - offset + len + 1, inOutEntities);
continue;
}
}
bool skip = false;
for (; i != e; ++i) { // find and check next finishing entity
if (i->offset() + i->length() > nextOffset) {
skip = (i->offset() < nextOffset + len);
break;
}
}
if (skip) {
moveStringPart(start, length, offset, nextOffset - offset + len, inOutEntities);
continue;
}
moveStringPart(start, length, offset, nextOffset - offset, inOutEntities);
*(start + length) = to;
++length;
offset += len;
}
if (length < s) result.resize(length);
}
QString prepareTextWithEntities(QString result, int32 flags, EntitiesInText *inOutEntities) {
cleanTextWithEntities(result, inOutEntities);
if (flags) {
textParseEntities(result, flags, inOutEntities);
}
replaceStringWithEntities(qstr("--"), QChar(8212), result, inOutEntities, true);
replaceStringWithEntities(qstr("<<"), QChar(171), result, inOutEntities);
replaceStringWithEntities(qstr(">>"), QChar(187), result, inOutEntities);
if (cReplaceEmojis()) {
result = replaceEmojis(result, inOutEntities);
}
trimTextWithEntities(result, inOutEntities);
return result;
}
// replace bad symbols with space and remove \r // replace bad symbols with space and remove \r
void cleanTextWithEntities(QString &result, EntitiesInText &entities) { void cleanTextWithEntities(QString &result, EntitiesInText *inOutEntities) {
result = result.replace('\t', qstr(" ")); result = result.replace('\t', qstr(" "));
int32 len = result.size(), to = 0, from = 0; int32 len = result.size(), to = 0, from = 0;
QChar *start = result.data(); QChar *start = result.data();
for (QChar *ch = start, *end = start + len; ch < end; ++ch) { for (QChar *ch = start, *end = start + len; ch < end; ++ch) {
if (ch->unicode() == '\r') { if (ch->unicode() == '\r') {
moveStringPart(start, to, from, (ch - start) - from, entities); moveStringPart(start, to, from, (ch - start) - from, inOutEntities);
++from; ++from;
} else if (chReplacedBySpace(*ch)) { } else if (chReplacedBySpace(*ch)) {
*ch = ' '; *ch = ' ';
} }
} }
moveStringPart(start, to, from, len - from, entities); moveStringPart(start, to, from, len - from, inOutEntities);
if (to < len) result.resize(to); if (to < len) result.resize(to);
} }
void trimTextWithEntities(QString &result, EntitiesInText &entities) { void trimTextWithEntities(QString &result, EntitiesInText *inOutEntities) {
bool foundNotTrimmed = false; bool foundNotTrimmedChar = false;
for (QChar *s = result.data(), *e = s + result.size(), *ch = e; ch != s;) { // rtrim
// right trim
for (QChar *s = result.data(), *e = s + result.size(), *ch = e; ch != s;) {
--ch; --ch;
if (!chIsTrimmed(*ch)) { if (!chIsTrimmed(*ch)) {
if (ch + 1 < e) { if (ch + 1 < e) {
int32 l = ch + 1 - s; int32 l = ch + 1 - s;
for (EntitiesInText::iterator i = entities.begin(), e = entities.end(); i != e; ++i) { for (auto &entity : *inOutEntities) {
if (i->offset > l) { entity.updateTextEnd(l);
i->offset = l;
i->length = 0;
} else if (i->offset + i->length > l) {
i->length = l - i->offset;
}
} }
result.resize(l); result.resize(l);
} }
foundNotTrimmed = true; foundNotTrimmedChar = true;
break; break;
} }
} }
if (!foundNotTrimmed) { if (!foundNotTrimmedChar) {
result.clear(); result.clear();
entities.clear(); inOutEntities->clear();
return; return;
} }
for (QChar *s = result.data(), *ch = s, *e = s + result.size(); ch != e; ++ch) { // ltrim int firstMonospaceOffset = EntityInText::firstMonospaceOffset(*inOutEntities, result.size());
if (!chIsTrimmed(*ch)) {
// left trim
for (QChar *s = result.data(), *ch = s, *e = s + result.size(); ch != e; ++ch) {
if (!chIsTrimmed(*ch) || (ch - s) == firstMonospaceOffset) {
if (ch > s) { if (ch > s) {
int32 l = ch - s; int32 l = ch - s;
for (EntitiesInText::iterator i = entities.begin(), e = entities.end(); i != e; ++i) { for (auto &entity : *inOutEntities) {
if (i->offset + i->length <= l) { entity.shiftLeft(l);
i->length = 0;
i->offset = 0;
} else if (i->offset < l) {
i->length = i->offset + i->length - l;
i->offset = 0;
} else {
i->offset -= l;
}
} }
result = result.mid(l); result = result.mid(l);
} }

View File

@ -21,11 +21,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
enum EntityInTextType { enum EntityInTextType {
EntityInTextInvalid = 0,
EntityInTextUrl, EntityInTextUrl,
EntityInTextCustomUrl, EntityInTextCustomUrl,
EntityInTextEmail, EntityInTextEmail,
EntityInTextHashtag, EntityInTextHashtag,
EntityInTextMention, EntityInTextMention,
EntityInTextMentionName,
EntityInTextBotCommand, EntityInTextBotCommand,
EntityInTextBold, EntityInTextBold,
@ -33,14 +36,94 @@ enum EntityInTextType {
EntityInTextCode, // inline EntityInTextCode, // inline
EntityInTextPre, // block EntityInTextPre, // block
}; };
struct EntityInText {
EntityInText(EntityInTextType type, int offset, int length, const QString &text = QString()) : type(type), offset(offset), length(length), text(text) { class EntityInText;
using EntitiesInText = QList<EntityInText>;
class EntityInText {
public:
EntityInText(EntityInTextType type, int offset, int length, const QString &data = QString())
: _type(type)
, _offset(offset)
, _length(length)
, _data(data) {
} }
EntityInTextType type;
int offset, length; EntityInTextType type() const {
QString text; return _type;
}
int offset() const {
return _offset;
}
int length() const {
return _length;
}
QString data() const {
return _data;
}
void extendToLeft(int extent) {
_offset -= extent;
_length += extent;
}
void shrinkFromRight(int shrink) {
_length -= shrink;
}
void shiftLeft(int shift) {
_offset -= shift;
if (_offset < 0) {
_length += _offset;
_offset = 0;
if (_length < 0) {
_length = 0;
}
}
}
void shiftRight(int shift) {
_offset += shift;
}
void updateTextEnd(int textEnd) {
if (_offset > textEnd) {
_offset = textEnd;
_length = 0;
} else if (_offset + _length > textEnd) {
_length = textEnd - _offset;
}
}
static int firstMonospaceOffset(const EntitiesInText &entities, int textLength) {
int result = textLength;
for_const (auto &entity, entities) {
if (entity.type() == EntityInTextPre || entity.type() == EntityInTextCode) {
accumulate_min(result, entity.offset());
}
}
return result;
}
explicit operator bool() const {
return type() != EntityInTextInvalid;
}
private:
EntityInTextType _type;
int _offset, _length;
QString _data;
}; };
typedef QList<EntityInText> EntitiesInText;
struct TextWithEntities {
QString text;
EntitiesInText entities;
};
inline void appendTextWithEntities(TextWithEntities &to, TextWithEntities &&append) {
int entitiesShiftRight = to.text.size();
for (auto &entity : append.entities) {
entity.shiftRight(entitiesShiftRight);
}
to.text += append.text;
to.entities += append.entities;
}
// text preprocess // text preprocess
QString textClean(const QString &text); QString textClean(const QString &text);
@ -65,21 +148,36 @@ enum {
TextInstagramHashtags = 0x800, TextInstagramHashtags = 0x800,
}; };
inline bool mentionNameToFields(const QString &data, int32 *outUserId, uint64 *outAccessHash) {
auto components = data.split('.');
if (!components.isEmpty()) {
*outUserId = components.at(0).toInt();
*outAccessHash = (components.size() > 1) ? components.at(1).toULongLong() : 0;
return (*outUserId != 0);
}
return false;
}
inline QString mentionNameFromFields(int32 userId, uint64 accessHash) {
return QString::number(userId) + '.' + QString::number(accessHash);
}
EntitiesInText entitiesFromMTP(const QVector<MTPMessageEntity> &entities); EntitiesInText entitiesFromMTP(const QVector<MTPMessageEntity> &entities);
MTPVector<MTPMessageEntity> linksToMTP(const EntitiesInText &links, bool sending = false); MTPVector<MTPMessageEntity> linksToMTP(const EntitiesInText &links, bool sending = false);
EntitiesInText textParseEntities(QString &text, int32 flags, bool rich = false); // changes text if (flags & TextParseMono) // New entities are added to the ones that are already in inOutEntities.
// Changes text if (flags & TextParseMono).
void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich = false);
QString textApplyEntities(const QString &text, const EntitiesInText &entities); QString textApplyEntities(const QString &text, const EntitiesInText &entities);
QString prepareTextWithEntities(QString result, EntitiesInText &entities, int32 flags); QString prepareTextWithEntities(QString result, int32 flags, EntitiesInText *inOutEntities);
inline QString prepareText(QString result, bool checkLinks = false) { inline QString prepareText(QString result, bool checkLinks = false) {
EntitiesInText entities; EntitiesInText entities;
return prepareTextWithEntities(result, entities, checkLinks ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0); auto prepareFlags = checkLinks ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0;
return prepareTextWithEntities(result, prepareFlags, &entities);
} }
void moveStringPart(QChar *start, int32 &to, int32 &from, int32 count, EntitiesInText &entities);
// replace bad symbols with space and remove \r // replace bad symbols with space and remove \r
void cleanTextWithEntities(QString &result, EntitiesInText &entities); void cleanTextWithEntities(QString &result, EntitiesInText *inOutEntities);
void trimTextWithEntities(QString &result, EntitiesInText &entities); void trimTextWithEntities(QString &result, EntitiesInText *inOutEntities);

View File

@ -125,6 +125,7 @@ SOURCES += \
./SourceFiles/dialogs/dialogs_indexed_list.cpp \ ./SourceFiles/dialogs/dialogs_indexed_list.cpp \
./SourceFiles/dialogs/dialogs_layout.cpp \ ./SourceFiles/dialogs/dialogs_layout.cpp \
./SourceFiles/dialogs/dialogs_list.cpp \ ./SourceFiles/dialogs/dialogs_list.cpp \
./SourceFiles/history/field_autocomplete.cpp \
./SourceFiles/inline_bots/inline_bot_layout_internal.cpp \ ./SourceFiles/inline_bots/inline_bot_layout_internal.cpp \
./SourceFiles/inline_bots/inline_bot_layout_item.cpp \ ./SourceFiles/inline_bots/inline_bot_layout_item.cpp \
./SourceFiles/inline_bots/inline_bot_result.cpp \ ./SourceFiles/inline_bots/inline_bot_result.cpp \
@ -248,6 +249,7 @@ HEADERS += \
./SourceFiles/dialogs/dialogs_list.h \ ./SourceFiles/dialogs/dialogs_list.h \
./SourceFiles/dialogs/dialogs_row.h \ ./SourceFiles/dialogs/dialogs_row.h \
./SourceFiles/history/history_common.h \ ./SourceFiles/history/history_common.h \
./SourceFiles/history/field_autocomplete.h \
./SourceFiles/inline_bots/inline_bot_layout_internal.h \ ./SourceFiles/inline_bots/inline_bot_layout_internal.h \
./SourceFiles/inline_bots/inline_bot_layout_item.h \ ./SourceFiles/inline_bots/inline_bot_layout_item.h \
./SourceFiles/inline_bots/inline_bot_result.h \ ./SourceFiles/inline_bots/inline_bot_result.h \

View File

@ -270,6 +270,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_field_autocomplete.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_fileuploader.cpp"> <ClCompile Include="GeneratedFiles\Debug\moc_fileuploader.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -557,6 +561,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_field_autocomplete.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_fileuploader.cpp"> <ClCompile Include="GeneratedFiles\Deploy\moc_fileuploader.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -870,6 +878,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_field_autocomplete.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_fileuploader.cpp"> <ClCompile Include="GeneratedFiles\Release\moc_fileuploader.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@ -1086,6 +1098,7 @@
<ClCompile Include="SourceFiles\fileuploader.cpp" /> <ClCompile Include="SourceFiles\fileuploader.cpp" />
<ClCompile Include="SourceFiles\history.cpp" /> <ClCompile Include="SourceFiles\history.cpp" />
<ClCompile Include="SourceFiles\historywidget.cpp" /> <ClCompile Include="SourceFiles\historywidget.cpp" />
<ClCompile Include="SourceFiles\history\field_autocomplete.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_internal.cpp" /> <ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_internal.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_item.cpp" /> <ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_item.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_result.cpp" /> <ClCompile Include="SourceFiles\inline_bots\inline_bot_result.cpp" />
@ -1266,6 +1279,20 @@
<ClInclude Include="SourceFiles\dialogs\dialogs_layout.h" /> <ClInclude Include="SourceFiles\dialogs\dialogs_layout.h" />
<ClInclude Include="SourceFiles\dialogs\dialogs_list.h" /> <ClInclude Include="SourceFiles\dialogs\dialogs_list.h" />
<ClInclude Include="SourceFiles\dialogs\dialogs_row.h" /> <ClInclude Include="SourceFiles\dialogs\dialogs_row.h" />
<CustomBuild Include="SourceFiles\history\field_autocomplete.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing field_autocomplete.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history/field_autocomplete.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing field_autocomplete.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history/field_autocomplete.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing field_autocomplete.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history/field_autocomplete.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
</CustomBuild>
<ClInclude Include="SourceFiles\history\history_common.h" /> <ClInclude Include="SourceFiles\history\history_common.h" />
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_internal.h" /> <ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_internal.h" />
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_item.h" /> <ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_item.h" />
@ -2307,7 +2334,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="Resources\telegram.qrc"> <CustomBuild Include="Resources\telegram.qrc">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(FullPath);.\Resources\art\icon256.png;%(AdditionalInputs)</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(FullPath);.\Resources\art\icon256.png;.\Resources\art\sprite_125x.png;.\Resources\art\sprite_150x.png;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Rcc%27ing %(Identity)...</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Rcc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\qrc_%(Filename).cpp;%(Outputs)</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\qrc_%(Filename).cpp;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\GeneratedFiles\qrc_%(Filename).cpp</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\GeneratedFiles\qrc_%(Filename).cpp</Command>
@ -2388,7 +2415,18 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</CodegenStyleItem> </CodegenStyleItem>
<CodegenStyleItem Include="Resources\basic.style" /> <CustomBuild Include="Resources\basic.style">
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(SolutionDir)$(Platform)\codegen\$(Configuration)\codegen_style.exe" "-I.\Resources" "-I.\SourceFiles" "-o.\GeneratedFiles\styles" %(FullPath)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(SolutionDir)$(Platform)\codegen\$(Configuration)\codegen_style.exe" "-I.\Resources" "-I.\SourceFiles" "-o.\GeneratedFiles\styles" %(FullPath)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(SolutionDir)$(Platform)\codegen\$(Configuration)\codegen_style.exe" "-I.\Resources" "-I.\SourceFiles" "-o.\GeneratedFiles\styles" %(FullPath)</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\styles\style_%(Filename).h;.\GeneratedFiles\styles\style_%(Filename).cpp;.\Resources\art\sprite_125x.png;.\Resources\art\sprite_150x.png;%(Outputs)</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\styles\style_%(Filename).h;.\GeneratedFiles\styles\style_%(Filename).cpp;.\Resources\art\sprite_125x.png;.\Resources\art\sprite_150x.png;%(Outputs)</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\styles\style_%(Filename).h;.\GeneratedFiles\styles\style_%(Filename).cpp;.\Resources\art\sprite_125x.png;.\Resources\art\sprite_150x.png;%(Outputs)</Outputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling style %(Identity)...</Message>
<Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Compiling style %(Identity)...</Message>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling style %(Identity)...</Message>
</CustomBuild>
<CodegenStyleItem Include="Resources\basic_types.style" /> <CodegenStyleItem Include="Resources\basic_types.style" />
<CodegenStyleItem Include="SourceFiles\overview\overview.style" /> <CodegenStyleItem Include="SourceFiles\overview\overview.style" />
</ItemGroup> </ItemGroup>

View File

@ -1104,6 +1104,18 @@
<ClCompile Include="SourceFiles\ui\style\style_core_icon.cpp"> <ClCompile Include="SourceFiles\ui\style\style_core_icon.cpp">
<Filter>SourceFiles\ui\style</Filter> <Filter>SourceFiles\ui\style</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_field_autocomplete.cpp">
<Filter>GeneratedFiles\Deploy</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\history\field_autocomplete.cpp">
<Filter>SourceFiles\history</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_field_autocomplete.cpp">
<Filter>GeneratedFiles\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_field_autocomplete.cpp">
<Filter>GeneratedFiles\Release</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h"> <ClInclude Include="SourceFiles\stdafx.h">
@ -1513,6 +1525,9 @@
<CustomBuild Include="SourceFiles\core\basic_types.h"> <CustomBuild Include="SourceFiles\core\basic_types.h">
<Filter>SourceFiles\core</Filter> <Filter>SourceFiles\core</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="SourceFiles\history\field_autocomplete.h">
<Filter>SourceFiles\history</Filter>
</CustomBuild>
<CustomBuild Include="Resources\langs\lang.strings"> <CustomBuild Include="Resources\langs\lang.strings">
<Filter>Resources\langs</Filter> <Filter>Resources\langs</Filter>
</CustomBuild> </CustomBuild>

View File

@ -75,6 +75,8 @@
076B1C5B1CBFC8F1002C0BC2 /* top_bar_widget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076B1C591CBFC8F1002C0BC2 /* top_bar_widget.cpp */; }; 076B1C5B1CBFC8F1002C0BC2 /* top_bar_widget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076B1C591CBFC8F1002C0BC2 /* top_bar_widget.cpp */; };
076B1C5F1CBFC98F002C0BC2 /* overview_layout.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076B1C5D1CBFC98F002C0BC2 /* overview_layout.cpp */; }; 076B1C5F1CBFC98F002C0BC2 /* overview_layout.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076B1C5D1CBFC98F002C0BC2 /* overview_layout.cpp */; };
076B1C631CBFCC53002C0BC2 /* moc_top_bar_widget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076B1C621CBFCC53002C0BC2 /* moc_top_bar_widget.cpp */; }; 076B1C631CBFCC53002C0BC2 /* moc_top_bar_widget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076B1C621CBFCC53002C0BC2 /* moc_top_bar_widget.cpp */; };
076C51D41CE205120038F22A /* field_autocomplete.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076C51D21CE205120038F22A /* field_autocomplete.cpp */; };
076C51D71CE2069F0038F22A /* moc_field_autocomplete.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 076C51D61CE2069F0038F22A /* moc_field_autocomplete.cpp */; };
077A4AF71CA41C38002188D2 /* connection_abstract.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 077A4AEC1CA41C38002188D2 /* connection_abstract.cpp */; }; 077A4AF71CA41C38002188D2 /* connection_abstract.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 077A4AEC1CA41C38002188D2 /* connection_abstract.cpp */; };
077A4AF81CA41C38002188D2 /* connection_auto.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 077A4AEE1CA41C38002188D2 /* connection_auto.cpp */; }; 077A4AF81CA41C38002188D2 /* connection_auto.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 077A4AEE1CA41C38002188D2 /* connection_auto.cpp */; };
077A4AF91CA41C38002188D2 /* connection_http.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 077A4AF01CA41C38002188D2 /* connection_http.cpp */; }; 077A4AF91CA41C38002188D2 /* connection_http.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 077A4AF01CA41C38002188D2 /* connection_http.cpp */; };
@ -396,6 +398,9 @@
076B1C5D1CBFC98F002C0BC2 /* overview_layout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = overview_layout.cpp; path = SourceFiles/overview/overview_layout.cpp; sourceTree = SOURCE_ROOT; }; 076B1C5D1CBFC98F002C0BC2 /* overview_layout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = overview_layout.cpp; path = SourceFiles/overview/overview_layout.cpp; sourceTree = SOURCE_ROOT; };
076B1C5E1CBFC98F002C0BC2 /* overview_layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = overview_layout.h; path = SourceFiles/overview/overview_layout.h; sourceTree = SOURCE_ROOT; }; 076B1C5E1CBFC98F002C0BC2 /* overview_layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = overview_layout.h; path = SourceFiles/overview/overview_layout.h; sourceTree = SOURCE_ROOT; };
076B1C621CBFCC53002C0BC2 /* moc_top_bar_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_top_bar_widget.cpp; path = GeneratedFiles/Debug/moc_top_bar_widget.cpp; sourceTree = SOURCE_ROOT; }; 076B1C621CBFCC53002C0BC2 /* moc_top_bar_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_top_bar_widget.cpp; path = GeneratedFiles/Debug/moc_top_bar_widget.cpp; sourceTree = SOURCE_ROOT; };
076C51D21CE205120038F22A /* field_autocomplete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = field_autocomplete.cpp; path = SourceFiles/history/field_autocomplete.cpp; sourceTree = SOURCE_ROOT; };
076C51D31CE205120038F22A /* field_autocomplete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = field_autocomplete.h; path = SourceFiles/history/field_autocomplete.h; sourceTree = SOURCE_ROOT; };
076C51D61CE2069F0038F22A /* moc_field_autocomplete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_field_autocomplete.cpp; path = GeneratedFiles/Debug/moc_field_autocomplete.cpp; sourceTree = SOURCE_ROOT; };
0771C4C94B623FC34BF62983 /* introwidget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = introwidget.cpp; path = SourceFiles/intro/introwidget.cpp; sourceTree = "<absolute>"; }; 0771C4C94B623FC34BF62983 /* introwidget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = introwidget.cpp; path = SourceFiles/intro/introwidget.cpp; sourceTree = "<absolute>"; };
077A4AEC1CA41C38002188D2 /* connection_abstract.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = connection_abstract.cpp; path = SourceFiles/mtproto/connection_abstract.cpp; sourceTree = SOURCE_ROOT; }; 077A4AEC1CA41C38002188D2 /* connection_abstract.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = connection_abstract.cpp; path = SourceFiles/mtproto/connection_abstract.cpp; sourceTree = SOURCE_ROOT; };
077A4AED1CA41C38002188D2 /* connection_abstract.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = connection_abstract.h; path = SourceFiles/mtproto/connection_abstract.h; sourceTree = SOURCE_ROOT; }; 077A4AED1CA41C38002188D2 /* connection_abstract.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = connection_abstract.h; path = SourceFiles/mtproto/connection_abstract.h; sourceTree = SOURCE_ROOT; };
@ -452,8 +457,6 @@
07BE85111A20961F008ACB9F /* moc_localstorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_localstorage.cpp; path = GeneratedFiles/Debug/moc_localstorage.cpp; sourceTree = SOURCE_ROOT; }; 07BE85111A20961F008ACB9F /* moc_localstorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_localstorage.cpp; path = GeneratedFiles/Debug/moc_localstorage.cpp; sourceTree = SOURCE_ROOT; };
07C3AF24194335ED0016CFF1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Telegram/Images.xcassets; sourceTree = SOURCE_ROOT; }; 07C3AF24194335ED0016CFF1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Telegram/Images.xcassets; sourceTree = SOURCE_ROOT; };
07C3AF27194336B90016CFF1 /* pspecific_mac_p.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pspecific_mac_p.h; path = SourceFiles/pspecific_mac_p.h; sourceTree = SOURCE_ROOT; }; 07C3AF27194336B90016CFF1 /* pspecific_mac_p.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pspecific_mac_p.h; path = SourceFiles/pspecific_mac_p.h; sourceTree = SOURCE_ROOT; };
07C3AF2919433ABF0016CFF1 /* style_classes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = style_classes.txt; path = Resources/style_classes.txt; sourceTree = SOURCE_ROOT; };
07C3AF2A19433ABF0016CFF1 /* style.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = style.txt; path = Resources/style.txt; sourceTree = SOURCE_ROOT; };
07C7596D1B1F7E0000662169 /* autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autoupdater.cpp; path = SourceFiles/autoupdater.cpp; sourceTree = SOURCE_ROOT; }; 07C7596D1B1F7E0000662169 /* autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autoupdater.cpp; path = SourceFiles/autoupdater.cpp; sourceTree = SOURCE_ROOT; };
07C7596E1B1F7E0000662169 /* autoupdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = autoupdater.h; path = SourceFiles/autoupdater.h; sourceTree = SOURCE_ROOT; }; 07C7596E1B1F7E0000662169 /* autoupdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = autoupdater.h; path = SourceFiles/autoupdater.h; sourceTree = SOURCE_ROOT; };
07C759711B1F7E2800662169 /* moc_autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_autoupdater.cpp; path = GeneratedFiles/Debug/moc_autoupdater.cpp; sourceTree = SOURCE_ROOT; }; 07C759711B1F7E2800662169 /* moc_autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_autoupdater.cpp; path = GeneratedFiles/Debug/moc_autoupdater.cpp; sourceTree = SOURCE_ROOT; };
@ -844,6 +847,7 @@
074968CB1A44D0B800394F46 /* langs */ = { 074968CB1A44D0B800394F46 /* langs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
07080BCB1A4357F300741A51 /* lang.strings */,
07A190511A723E0A004287AE /* lang_ko.strings */, 07A190511A723E0A004287AE /* lang_ko.strings */,
072E117A1A56EB9400A87ACC /* lang_pt_BR.strings */, 072E117A1A56EB9400A87ACC /* lang_pt_BR.strings */,
078DD0241A48DD9E00DD14CC /* lang_de.strings */, 078DD0241A48DD9E00DD14CC /* lang_de.strings */,
@ -884,6 +888,8 @@
076B1C561CBFC8C9002C0BC2 /* history */ = { 076B1C561CBFC8C9002C0BC2 /* history */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
076C51D21CE205120038F22A /* field_autocomplete.cpp */,
076C51D31CE205120038F22A /* field_autocomplete.h */,
076B1C571CBFC8D9002C0BC2 /* history_common.h */, 076B1C571CBFC8D9002C0BC2 /* history_common.h */,
); );
name = history; name = history;
@ -911,10 +917,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
074968CB1A44D0B800394F46 /* langs */, 074968CB1A44D0B800394F46 /* langs */,
07080BCB1A4357F300741A51 /* lang.strings */,
0747FF811CC644FF00096FC3 /* numbers.txt */, 0747FF811CC644FF00096FC3 /* numbers.txt */,
07C3AF2919433ABF0016CFF1 /* style_classes.txt */,
07C3AF2A19433ABF0016CFF1 /* style.txt */,
07AF95F71AFD03C80060B057 /* telegram_emojis.qrc */, 07AF95F71AFD03C80060B057 /* telegram_emojis.qrc */,
07AF95F81AFD03C80060B057 /* telegram_mac.qrc */, 07AF95F81AFD03C80060B057 /* telegram_mac.qrc */,
1292B92B4848460640F6A391 /* telegram.qrc */, 1292B92B4848460640F6A391 /* telegram.qrc */,
@ -1207,6 +1210,7 @@
801973D3334D0FCA849CF485 /* Debug */ = { 801973D3334D0FCA849CF485 /* Debug */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
076C51D61CE2069F0038F22A /* moc_field_autocomplete.cpp */,
076B1C621CBFCC53002C0BC2 /* moc_top_bar_widget.cpp */, 076B1C621CBFCC53002C0BC2 /* moc_top_bar_widget.cpp */,
07C8FE111CB80915007A8702 /* moc_toast_manager.cpp */, 07C8FE111CB80915007A8702 /* moc_toast_manager.cpp */,
077A4AFF1CA41EE2002188D2 /* moc_connection_abstract.cpp */, 077A4AFF1CA41EE2002188D2 /* moc_connection_abstract.cpp */,
@ -1675,6 +1679,7 @@
77DA1217B595B799FB72CDDA /* flatinput.cpp in Compile Sources */, 77DA1217B595B799FB72CDDA /* flatinput.cpp in Compile Sources */,
DE6A34CA3A5561888FA01AF1 /* flatlabel.cpp in Compile Sources */, DE6A34CA3A5561888FA01AF1 /* flatlabel.cpp in Compile Sources */,
07C8FE041CB66D97007A8702 /* inline_bot_send_data.cpp in Compile Sources */, 07C8FE041CB66D97007A8702 /* inline_bot_send_data.cpp in Compile Sources */,
076C51D41CE205120038F22A /* field_autocomplete.cpp in Compile Sources */,
03270F718426CFE84729079E /* flattextarea.cpp in Compile Sources */, 03270F718426CFE84729079E /* flattextarea.cpp in Compile Sources */,
E3D7A5CA24541D5DB69D6606 /* images.cpp in Compile Sources */, E3D7A5CA24541D5DB69D6606 /* images.cpp in Compile Sources */,
ADE99904299B99EB6135E8D9 /* scrollarea.cpp in Compile Sources */, ADE99904299B99EB6135E8D9 /* scrollarea.cpp in Compile Sources */,
@ -1771,6 +1776,7 @@
07DE92AD1AA4928B00A18F6F /* moc_passcodebox.cpp in Compile Sources */, 07DE92AD1AA4928B00A18F6F /* moc_passcodebox.cpp in Compile Sources */,
FCC949FEA178F9F5D7478027 /* moc_flattextarea.cpp in Compile Sources */, FCC949FEA178F9F5D7478027 /* moc_flattextarea.cpp in Compile Sources */,
07DB674D1AD07C9200A51329 /* abstractbox.cpp in Compile Sources */, 07DB674D1AD07C9200A51329 /* abstractbox.cpp in Compile Sources */,
076C51D71CE2069F0038F22A /* moc_field_autocomplete.cpp in Compile Sources */,
3F6EB1F5B98E704960FEA686 /* moc_scrollarea.cpp in Compile Sources */, 3F6EB1F5B98E704960FEA686 /* moc_scrollarea.cpp in Compile Sources */,
60CB4898955209B665E7B07D /* moc_twidget.cpp in Compile Sources */, 60CB4898955209B665E7B07D /* moc_twidget.cpp in Compile Sources */,
077A4B051CA41EE2002188D2 /* moc_connection_http.cpp in Compile Sources */, 077A4B051CA41EE2002188D2 /* moc_connection_http.cpp in Compile Sources */,
@ -1903,11 +1909,13 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CRASHPAD_PATH = ./../../Libraries/crashpad/crashpad;
CURRENT_PROJECT_VERSION = "$(TDESKTOP_VERSION)"; CURRENT_PROJECT_VERSION = "$(TDESKTOP_VERSION)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = "$(TDESKTOP_MAJOR_VERSION)"; DYLIB_COMPATIBILITY_VERSION = "$(TDESKTOP_MAJOR_VERSION)";
DYLIB_CURRENT_VERSION = "$(TDESKTOP_VERSION)"; DYLIB_CURRENT_VERSION = "$(TDESKTOP_VERSION)";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
FFMPEG_PATH = /usr/local;
FRAMEWORK_SEARCH_PATHS = ""; FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_LINK_WITH_DYNAMIC_LIBRARIES = NO; GCC_LINK_WITH_DYNAMIC_LIBRARIES = NO;
@ -1940,6 +1948,7 @@
"$(CRASHPAD_PATH)", "$(CRASHPAD_PATH)",
"$(CRASHPAD_PATH)/third_party/mini_chromium/mini_chromium", "$(CRASHPAD_PATH)/third_party/mini_chromium/mini_chromium",
); );
ICONV_PATH = /usr/local;
INFOPLIST_FILE = Telegram.plist; INFOPLIST_FILE = Telegram.plist;
INSTALL_DIR = ./../Mac/Release/; INSTALL_DIR = ./../Mac/Release/;
LDPLUSPLUS = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++"; LDPLUSPLUS = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++";
@ -1954,6 +1963,7 @@
); );
MACOSX_DEPLOYMENT_TARGET = 10.8; MACOSX_DEPLOYMENT_TARGET = 10.8;
OBJROOT = "./../Mac/$(CONFIGURATION)Intermediate"; OBJROOT = "./../Mac/$(CONFIGURATION)Intermediate";
OPENAL_PATH = /usr/local;
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-pipe", "-pipe",
"-g", "-g",
@ -2015,15 +2025,11 @@
PRODUCT_NAME = Telegram; PRODUCT_NAME = Telegram;
QT_LIBRARY_SUFFIX = ""; QT_LIBRARY_SUFFIX = "";
QT_PATH = "/usr/local/tdesktop/Qt-5.6.0"; QT_PATH = "/usr/local/tdesktop/Qt-5.6.0";
ZLIB_PATH = "/usr/local";
FFMPEG_PATH = "/usr/local";
ICONV_PATH = "/usr/local";
CRASHPAD_PATH = "./../../Libraries/crashpad/crashpad";
OPENAL_PATH = "/usr/local";
SDKROOT = macosx; SDKROOT = macosx;
SYMROOT = ./../Mac; SYMROOT = ./../Mac;
TDESKTOP_MAJOR_VERSION = 0.9; TDESKTOP_MAJOR_VERSION = 0.9;
TDESKTOP_VERSION = 0.9.48; TDESKTOP_VERSION = 0.9.49;
ZLIB_PATH = /usr/local;
}; };
name = Release; name = Release;
}; };
@ -2042,12 +2048,14 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CRASHPAD_PATH = ./../../Libraries/crashpad/crashpad;
CURRENT_PROJECT_VERSION = "$(TDESKTOP_VERSION)"; CURRENT_PROJECT_VERSION = "$(TDESKTOP_VERSION)";
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = "$(TDESKTOP_MAJOR_VERSION)"; DYLIB_COMPATIBILITY_VERSION = "$(TDESKTOP_MAJOR_VERSION)";
DYLIB_CURRENT_VERSION = "$(TDESKTOP_VERSION)"; DYLIB_CURRENT_VERSION = "$(TDESKTOP_VERSION)";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
FFMPEG_PATH = /usr/local;
FRAMEWORK_SEARCH_PATHS = ""; FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_LINK_WITH_DYNAMIC_LIBRARIES = NO; GCC_LINK_WITH_DYNAMIC_LIBRARIES = NO;
@ -2080,6 +2088,7 @@
"$(CRASHPAD_PATH)", "$(CRASHPAD_PATH)",
"$(CRASHPAD_PATH)/third_party/mini_chromium/mini_chromium", "$(CRASHPAD_PATH)/third_party/mini_chromium/mini_chromium",
); );
ICONV_PATH = /usr/local;
INFOPLIST_FILE = Telegram.plist; INFOPLIST_FILE = Telegram.plist;
INSTALL_DIR = ./../Mac/Debug/; INSTALL_DIR = ./../Mac/Debug/;
LDPLUSPLUS = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++"; LDPLUSPLUS = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++";
@ -2095,6 +2104,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.8; MACOSX_DEPLOYMENT_TARGET = 10.8;
OBJROOT = "./../Mac/$(CONFIGURATION)Intermediate"; OBJROOT = "./../Mac/$(CONFIGURATION)Intermediate";
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OPENAL_PATH = /usr/local;
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-pipe", "-pipe",
"-g", "-g",
@ -2156,15 +2166,11 @@
PRODUCT_NAME = Telegram; PRODUCT_NAME = Telegram;
QT_LIBRARY_SUFFIX = _debug; QT_LIBRARY_SUFFIX = _debug;
QT_PATH = "/usr/local/tdesktop/Qt-5.6.0"; QT_PATH = "/usr/local/tdesktop/Qt-5.6.0";
ZLIB_PATH = "/usr/local";
FFMPEG_PATH = "/usr/local";
ICONV_PATH = "/usr/local";
CRASHPAD_PATH = "./../../Libraries/crashpad/crashpad";
OPENAL_PATH = "/usr/local";
SDKROOT = macosx; SDKROOT = macosx;
SYMROOT = ./../Mac; SYMROOT = ./../Mac;
TDESKTOP_MAJOR_VERSION = 0.9; TDESKTOP_MAJOR_VERSION = 0.9;
TDESKTOP_VERSION = 0.9.48; TDESKTOP_VERSION = 0.9.49;
ZLIB_PATH = /usr/local;
}; };
name = Debug; name = Debug;
}; };

View File

@ -111,6 +111,7 @@ compilers: GeneratedFiles/qrc_telegram.cpp\
GeneratedFiles/Debug/moc_sessionsbox.cpp\ GeneratedFiles/Debug/moc_sessionsbox.cpp\
GeneratedFiles/Debug/moc_stickersetbox.cpp\ GeneratedFiles/Debug/moc_stickersetbox.cpp\
GeneratedFiles/Debug/moc_usernamebox.cpp\ GeneratedFiles/Debug/moc_usernamebox.cpp\
GeneratedFiles/Debug/moc_field_autocomplete.cpp\
GeneratedFiles/Debug/moc_introwidget.cpp\ GeneratedFiles/Debug/moc_introwidget.cpp\
GeneratedFiles/Debug/moc_introcode.cpp\ GeneratedFiles/Debug/moc_introcode.cpp\
GeneratedFiles/Debug/moc_introphone.cpp\ GeneratedFiles/Debug/moc_introphone.cpp\
@ -237,6 +238,7 @@ compiler_moc_header_make_all: GeneratedFiles/Debug/moc_apiwrap.cpp\
GeneratedFiles/Debug/moc_sessionsbox.cpp\ GeneratedFiles/Debug/moc_sessionsbox.cpp\
GeneratedFiles/Debug/moc_stickersetbox.cpp\ GeneratedFiles/Debug/moc_stickersetbox.cpp\
GeneratedFiles/Debug/moc_usernamebox.cpp\ GeneratedFiles/Debug/moc_usernamebox.cpp\
GeneratedFiles/Debug/moc_field_autocomplete.cpp\
GeneratedFiles/Debug/moc_introwidget.cpp\ GeneratedFiles/Debug/moc_introwidget.cpp\
GeneratedFiles/Debug/moc_introcode.cpp\ GeneratedFiles/Debug/moc_introcode.cpp\
GeneratedFiles/Debug/moc_introphone.cpp\ GeneratedFiles/Debug/moc_introphone.cpp\
@ -306,6 +308,7 @@ compiler_moc_header_clean:
GeneratedFiles/Debug/moc_sessionsbox.cpp\ GeneratedFiles/Debug/moc_sessionsbox.cpp\
GeneratedFiles/Debug/moc_stickersetbox.cpp\ GeneratedFiles/Debug/moc_stickersetbox.cpp\
GeneratedFiles/Debug/moc_usernamebox.cpp\ GeneratedFiles/Debug/moc_usernamebox.cpp\
GeneratedFiles/Debug/moc_field_autocomplete.cpp\
GeneratedFiles/Debug/moc_introwidget.cpp\ GeneratedFiles/Debug/moc_introwidget.cpp\
GeneratedFiles/Debug/moc_introcode.cpp\ GeneratedFiles/Debug/moc_introcode.cpp\
GeneratedFiles/Debug/moc_introphone.cpp\ GeneratedFiles/Debug/moc_introphone.cpp\
@ -498,6 +501,9 @@ GeneratedFiles/Debug/moc_stickersetbox.cpp: SourceFiles/boxes/stickersetbox.h
GeneratedFiles/Debug/moc_usernamebox.cpp:SourceFiles/boxes/usernamebox.h GeneratedFiles/Debug/moc_usernamebox.cpp:SourceFiles/boxes/usernamebox.h
$(MOC_FILE) SourceFiles/boxes/usernamebox.h -o GeneratedFiles/Debug/moc_usernamebox.cpp $(MOC_FILE) SourceFiles/boxes/usernamebox.h -o GeneratedFiles/Debug/moc_usernamebox.cpp
GeneratedFiles/Debug/moc_field_autocomplete.cpp: SourceFiles/history/field_autocomplete.h
$(MOC_FILE) SourceFiles/history/field_autocomplete.h -o GeneratedFiles/Debug/moc_field_autocomplete.cpp
GeneratedFiles/Debug/moc_introwidget.cpp: SourceFiles/intro/introwidget.h GeneratedFiles/Debug/moc_introwidget.cpp: SourceFiles/intro/introwidget.h
$(MOC_FILE) SourceFiles/intro/introwidget.h -o GeneratedFiles/Debug/moc_introwidget.cpp $(MOC_FILE) SourceFiles/intro/introwidget.h -o GeneratedFiles/Debug/moc_introwidget.cpp

View File

@ -1,6 +1,6 @@
AppVersion 9048 AppVersion 9049
AppVersionStrMajor 0.9 AppVersionStrMajor 0.9
AppVersionStrSmall 0.9.48 AppVersionStrSmall 0.9.49
AppVersionStr 0.9.48 AppVersionStr 0.9.49
AlphaChannel 0 AlphaChannel 0
BetaVersion 0 BetaVersion 0