diff --git a/.gitignore b/.gitignore
index 94b1d353e..49c8fc4d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,14 @@
/Telegram/SourceFiles/art/grid_200x.png
/Telegram/SourceFiles/art/sprite_125x.png
/Telegram/SourceFiles/art/sprite_150x.png
+/Telegram/Resources/art/grid.png
+/Telegram/Resources/art/grid_125x.png
+/Telegram/Resources/art/grid_150x.png
+/Telegram/Resources/art/grid_200x.png
+/Telegram/Resources/art/sprite_125x.png
+/Telegram/Resources/art/sprite_150x.png
/Telegram/*.user
+*.vcxproj.user
*.suo
*.sdf
*.opensdf
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..11f305e48
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,47 @@
+sudo: required
+
+language: cpp
+
+env:
+ - BUILD_VERSION=""
+ - BUILD_VERSION="disable_autoupdate"
+ - BUILD_VERSION="disable_register_custom_scheme"
+ - BUILD_VERSION="disable_crash_reports"
+ - BUILD_VERSION="disable_network_proxy"
+
+arch:
+ packages:
+ - bzr
+ - wget
+ - qt5-base
+
+ - git
+ - patch
+ - libunity
+ - libappindicator-gtk2
+
+ - ffmpeg
+ - icu
+ - jasper
+ - libexif
+ - libmng
+ - libwebp
+ - libxkbcommon-x11
+ - libinput
+ - libproxy
+ - mtdev
+ - openal
+ - libva
+ - desktop-file-utils
+ - gtk-update-icon-cache
+
+ script:
+ - libtool --finish /usr/lib
+ - .travis/build.sh
+
+before_install:
+ - "export TRAVIS_COMMIT_MSG=\"$(git log --format=%B --no-merges -n 1)\""
+ - .travis/check.sh
+
+script:
+ - .travis/arch.sh
\ No newline at end of file
diff --git a/.travis/arch.sh b/.travis/arch.sh
new file mode 100755
index 000000000..9c917be1d
--- /dev/null
+++ b/.travis/arch.sh
@@ -0,0 +1,297 @@
+#!/bin/bash
+# Copyright (C) 2016 Mikkel Oscar Lyderik Larsen
+#
+# This program 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.
+#
+# This program 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# Source: https://raw.githubusercontent.com/mikkeloscar/arch-travis/master/arch-travis.sh
+
+# Script for setting up and running a travis-ci build in an up to date
+# Arch Linux chroot
+
+ARCH_TRAVIS_MIRROR=${ARCH_TRAVIS_MIRROR:-"https://lug.mtu.edu/archlinux"}
+ARCH_TRAVIS_ARCH_ISO=${ARCH_TRAVIS_ARCH_ISO:-"$(date +%Y.%m).01"}
+mirror_entry='Server = '$ARCH_TRAVIS_MIRROR'/\$repo/os/\$arch'
+archive="archlinux-bootstrap-$ARCH_TRAVIS_ARCH_ISO-x86_64.tar.gz"
+default_root="root.x86_64"
+ARCH_TRAVIS_CHROOT=${ARCH_TRAVIS_CHROOT:-"$default_root"}
+user="travis"
+user_home="/home/$user"
+user_build_dir="/build"
+user_uid=$UID
+
+if [ -n "$CC" ]; then
+ # store travis CC
+ TRAVIS_CC=$CC
+ # reset to gcc for building arch packages
+ CC=gcc
+fi
+
+
+# default packages
+default_packages=("base-devel" "git")
+
+# pacman.conf repository line
+repo_line=70
+
+# setup working Arch Linux chroot
+setup_chroot() {
+ arch_msg "Setting up Arch chroot"
+
+ if [ ! -f $archive ]; then
+ # get root fs
+ curl --fail -O "$ARCH_TRAVIS_MIRROR/iso/$ARCH_TRAVIS_ARCH_ISO/$archive" 2>&1
+ local ret=$?
+
+ # if it fails, try arch iso form the previous month
+ if [ $ret -gt 0 ]; then
+ ARCH_TRAVIS_ARCH_ISO="$(date +%Y.%m -d "-1 month").01"
+ archive="archlinux-bootstrap-$ARCH_TRAVIS_ARCH_ISO-x86_64.tar.gz"
+ as_normal "curl -O $ARCH_TRAVIS_MIRROR/iso/$ARCH_TRAVIS_ARCH_ISO/$archive"
+ fi
+ fi
+
+ # extract root fs
+ as_root "tar xf $archive"
+
+ # remove archive if ARCH_TRAVIS_CLEAN_CHROOT is set
+ if [ -n "$ARCH_TRAVIS_CLEAN_CHROOT" ]; then
+ as_root "rm $archive"
+ fi
+
+ if [ "$ARCH_TRAVIS_CHROOT" != "$default_root" ]; then
+ as_root "mv $default_root $ARCH_TRAVIS_CHROOT"
+ fi
+
+ # don't care for signed packages
+ as_root "sed -i 's|SigLevel = Required DatabaseOptional|SigLevel = Never|' $ARCH_TRAVIS_CHROOT/etc/pacman.conf"
+
+ # enable multilib
+ as_root "sed -i 's|#\[multilib\]|\[multilib\]\nInclude = /etc/pacman.d/mirrorlist|' $ARCH_TRAVIS_CHROOT/etc/pacman.conf"
+
+ # add mirror
+ as_root "echo $mirror_entry >> $ARCH_TRAVIS_CHROOT/etc/pacman.d/mirrorlist"
+
+ # add nameserver to resolv.conf
+ as_root "echo nameserver 8.8.8.8 >> $ARCH_TRAVIS_CHROOT/etc/resolv.conf"
+
+ sudo mount $ARCH_TRAVIS_CHROOT $ARCH_TRAVIS_CHROOT --bind
+ sudo mount --bind /proc $ARCH_TRAVIS_CHROOT/proc
+ sudo mount --bind /sys $ARCH_TRAVIS_CHROOT/sys
+ sudo mount --bind /dev $ARCH_TRAVIS_CHROOT/dev
+ sudo mount --bind /dev/pts $ARCH_TRAVIS_CHROOT/dev/pts
+ sudo mount --bind /dev/shm $ARCH_TRAVIS_CHROOT/dev/shm
+ sudo mount --bind /run $ARCH_TRAVIS_CHROOT/run
+
+ # update packages
+ chroot_as_root "pacman -Syy"
+ chroot_as_root "pacman -Syu ${default_packages[*]} --noconfirm"
+
+ # use LANG=en_US.UTF-8 as expected in travis environments
+ as_root "sed -i 's|#en_US.UTF-8|en_US.UTF-8|' $ARCH_TRAVIS_CHROOT/etc/locale.gen"
+ chroot_as_root "locale-gen"
+
+ # setup non-root user
+ chroot_as_root "useradd -u $user_uid -m -s /bin/bash $user"
+
+ # disable password for sudo users
+ as_root "echo \"$user ALL=(ALL) NOPASSWD: ALL\" >> $ARCH_TRAVIS_CHROOT/etc/sudoers.d/$user"
+
+ # Add build dir
+ chroot_as_root "mkdir $user_build_dir && chown $user $user_build_dir"
+
+ # bind $TRAVIS_BUILD_DIR to chroot build dir
+ sudo mount --bind $TRAVIS_BUILD_DIR $ARCH_TRAVIS_CHROOT$user_build_dir
+
+ # add custom repos
+ add_repositories
+
+ # setup pacaur for AUR packages
+ setup_pacaur
+}
+
+# add custom repositories to pacman.conf
+add_repositories() {
+ if [ ${#CONFIG_REPOS[@]} -gt 0 ]; then
+ for r in "${CONFIG_REPOS[@]}"; do
+ local splitarr=(${r//=/ })
+ ((repo_line+=1))
+ as_root "sed -i '${repo_line}i[${splitarr[0]}]' $ARCH_TRAVIS_CHROOT/etc/pacman.conf"
+ ((repo_line+=1))
+ as_root "sed -i '${repo_line}iServer = ${splitarr[1]}\n' $ARCH_TRAVIS_CHROOT/etc/pacman.conf"
+ ((repo_line+=1))
+ done
+
+ # update repos
+ chroot_as_root "pacman -Syy"
+ fi
+}
+
+# a wrapper which can be used to eventually add fakeroot support.
+sudo_wrapper() {
+ sudo "$@"
+}
+
+# run command as normal user
+as_normal() {
+ local str="$@"
+ run /bin/bash -c "$str"
+}
+
+# run command as root
+as_root() {
+ local str="$@"
+ run sudo_wrapper /bin/bash -c "$str"
+}
+
+# run command in chroot as root
+chroot_as_root() {
+ local str="$@"
+ run sudo_wrapper chroot $ARCH_TRAVIS_CHROOT /bin/bash -c "$str"
+}
+
+# run command in chroot as normal user
+chroot_as_normal() {
+ local str="$@"
+ run sudo_wrapper chroot --userspec=$user:$user $ARCH_TRAVIS_CHROOT /bin/bash \
+ -c "export HOME=$user_home USER=$user TRAVIS_BUILD_DIR=$user_build_dir && cd $user_build_dir && $str"
+}
+
+# run command
+run() {
+ "$@"
+ local ret=$?
+
+ if [ $ret -gt 0 ]; then
+ takedown_chroot
+ exit $ret
+ fi
+}
+
+# run build script
+run_build_script() {
+ local cmd="$@"
+ echo "$ $cmd"
+ sudo_wrapper chroot --userspec=$user:$user $ARCH_TRAVIS_CHROOT /bin/bash -c "export HOME=$user_home USER=$user TRAVIS_BUILD_DIR=$user_build_dir && cd $user_build_dir && $cmd"
+ local ret=$?
+
+ if [ $ret -gt 0 ]; then
+ takedown_chroot
+ exit $ret
+ fi
+}
+
+# setup pacaur
+setup_pacaur() {
+ local cowerarchive="cower.tar.gz"
+ local aururl="https://aur.archlinux.org/cgit/aur.git/snapshot/"
+ # install cower
+ as_normal "curl -O $aururl/$cowerarchive"
+ as_normal "tar xf $cowerarchive"
+ chroot_as_normal "cd cower && makepkg -is --skippgpcheck --noconfirm"
+ as_root "rm -r cower"
+ as_normal "rm $cowerarchive"
+ # install pacaur
+ chroot_as_normal "cower -dd pacaur"
+ chroot_as_normal "cd pacaur && makepkg -is --noconfirm"
+ chroot_as_normal "rm -rf pacaur"
+}
+
+# install package through pacaur
+_pacaur() {
+ local pacaur="pacaur -S $@ --noconfirm --noedit"
+ chroot_as_normal "$pacaur"
+}
+
+# takedown chroot
+# unmounts anything mounted in the chroot setup
+takedown_chroot() {
+ sudo umount $ARCH_TRAVIS_CHROOT/{run,dev/shm,dev/pts,dev,sys,proc}
+ sudo umount $ARCH_TRAVIS_CHROOT$user_build_dir
+ sudo umount $ARCH_TRAVIS_CHROOT
+
+ if [ -n "$ARCH_TRAVIS_CLEAN_CHROOT" ]; then
+ as_root "rm -rf $ARCH_TRAVIS_CHROOT"
+ fi
+}
+
+# read value from .travis.yml
+travis_yml() {
+ ruby -ryaml -e 'puts ARGV[1..-1].inject(YAML.load(File.read(ARGV[0]))) {|acc, key| acc[key] }' .travis.yml $@
+}
+
+read_config() {
+ old_ifs=$IFS
+ IFS=$'\n'
+ CONFIG_BUILD_SCRIPTS=($(travis_yml arch script))
+ CONFIG_PACKAGES=($(travis_yml arch packages))
+ CONFIG_REPOS=($(travis_yml arch repos))
+ IFS=$old_ifs
+}
+
+# run build scripts defined in .travis.yml
+build_scripts() {
+ if [ ${#CONFIG_BUILD_SCRIPTS[@]} -gt 0 ]; then
+ for script in "${CONFIG_BUILD_SCRIPTS[@]}"; do
+ run_build_script $script
+ done
+ else
+ echo "No build scripts defined"
+ takedown_chroot
+ exit 1
+ fi
+}
+
+# install packages defined in .travis.yml
+install_packages() {
+ for package in "${CONFIG_PACKAGES[@]}"; do
+ _pacaur $package
+ done
+}
+
+# install custom compiler if CC != gcc
+install_c_compiler() {
+ if [ "$TRAVIS_CC" != "gcc" ]; then
+ _pacaur "$TRAVIS_CC"
+ fi
+}
+
+arch_msg() {
+ lightblue='\033[1;34m'
+ reset='\e[0m'
+ echo -e "${lightblue}$@${reset}"
+}
+
+# read .travis.yml
+read_config
+
+echo "travis_fold:start:arch_travis"
+setup_chroot
+
+install_packages
+
+if [ -n "$CC" ]; then
+ install_c_compiler
+
+ # restore CC
+ CC=$TRAVIS_CC
+fi
+echo "travis_fold:end:arch_travis"
+echo ""
+
+arch_msg "Running travis build"
+build_scripts
+
+takedown_chroot
+
+# vim:set ts=2 sw=2 et:
diff --git a/.travis/build.sh b/.travis/build.sh
new file mode 100755
index 000000000..7deae1f95
--- /dev/null
+++ b/.travis/build.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+# Installs libs and compiles tdesktop
+
+run() {
+ info_msg "Build version: ${BUILD_VERSION}"
+
+ downloadLibs
+ prepare
+ build
+ check
+}
+
+downloadLibs() {
+ travis_fold_start "download_libs"
+ # Move telegram project to subfolder
+ mkdir tdesktop
+ mv -f Telegram tdesktop
+
+ # Download libraries
+ info_msg "QT-Version: ${_qtver}, SRC-Dir: ${srcdir}"
+
+ echo -e "\nDownload and extract qt"
+ qt_file=qt-everywhere-opensource-src-$_qtver.tar.xz
+ echo -e "QT-File: ${qt_file}"
+
+ wget "http://download.qt.io/official_releases/qt/${_qtver%.*}/$_qtver/single/$qt_file"
+ tar xf $qt_file
+ rm $qt_file
+
+ echo -e "Clone Breakpad"
+ git clone https://chromium.googlesource.com/breakpad/breakpad breakpad
+
+ echo -e "\nClone Linux Syscall Support"
+ git clone https://chromium.googlesource.com/linux-syscall-support breakpad-lss
+
+ echo -e "\nLets view the folder content"
+ ls
+ travis_fold_end "download_libs"
+}
+
+prepare() {
+ travis_fold_start "prepare"
+ start_msg "Preparing the libraries..."
+
+ cd "$srcdir/tdesktop"
+
+ mkdir -p "$srcdir/Libraries"
+
+ local qt_patch_file="$srcdir/tdesktop/Telegram/_qtbase_${_qtver//./_}_patch.diff"
+ if [ "$qt_patch_file" -nt "$srcdir/Libraries/QtStatic" ]; then
+ rm -rf "$srcdir/Libraries/QtStatic"
+ mv "$srcdir/qt-everywhere-opensource-src-$_qtver" "$srcdir/Libraries/QtStatic"
+ cd "$srcdir/Libraries/QtStatic/qtbase"
+ patch -p1 -i "$qt_patch_file"
+ fi
+
+ if [ ! -h "$srcdir/Libraries/breakpad" ]; then
+ ln -s "$srcdir/breakpad" "$srcdir/Libraries/breakpad"
+ ln -s "$srcdir/breakpad-lss" "$srcdir/Libraries/breakpad/src/third_party/lss"
+ fi
+
+ sed -i 's/CUSTOM_API_ID//g' "$srcdir/tdesktop/Telegram/Telegram.pro"
+ sed -i 's,LIBS += /usr/local/lib/libxkbcommon.a,,g' "$srcdir/tdesktop/Telegram/Telegram.pro"
+ sed -i 's,LIBS += /usr/local/lib/libz.a,LIBS += -lz,g' "$srcdir/tdesktop/Telegram/Telegram.pro"
+
+ local options=""
+
+ if [[ $BUILD_VERSION == *"disable_autoupdate"* ]]; then
+ options+="\nDEFINES += TDESKTOP_DISABLE_AUTOUPDATE"
+ fi
+
+ if [[ $BUILD_VERSION == *"disable_register_custom_scheme"* ]]; then
+ options+="\nDEFINES += TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME"
+ fi
+
+ if [[ $BUILD_VERSION == *"disable_crash_reports"* ]]; then
+ options+="\nDEFINES += TDESKTOP_DISABLE_CRASH_REPORTS"
+ fi
+
+ if [[ $BUILD_VERSION == *"disable_network_proxy"* ]]; then
+ options+="\nDEFINES += TDESKTOP_DISABLE_NETWORK_PROXY"
+ fi
+
+ options+='\nINCLUDEPATH += "/usr/lib/glib-2.0/include"'
+ options+='\nINCLUDEPATH += "/usr/lib/gtk-2.0/include"'
+ options+='\nINCLUDEPATH += "/usr/include/opus"'
+ options+='\nLIBS += -lcrypto -lssl'
+
+ info_msg "Build options: ${options}"
+
+ echo -e "${options}" >> "$srcdir/tdesktop/Telegram/Telegram.pro"
+
+ success_msg "Prepare done! :)"
+ travis_fold_end "prepare"
+}
+
+build() {
+ start_msg "Building the projects..."
+
+ info_msg "Build patched Qt"
+ # Build patched Qt
+ cd "$srcdir/Libraries/QtStatic"
+ ./configure -prefix "$srcdir/qt" -release -opensource -confirm-license -qt-zlib \
+ -qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb \
+ -qt-xkbcommon-x11 -no-opengl -static -nomake examples -nomake tests
+ make --silent module-qtbase module-qtimageformats
+ make --silent module-qtbase-install_subtargets module-qtimageformats-install_subtargets
+
+ export PATH="$srcdir/qt/bin:$PATH"
+
+ info_msg "Build breakpad"
+ # Build breakpad
+ cd "$srcdir/Libraries/breakpad"
+ ./configure
+ make --silent
+
+ info_msg "Build MetaStyle"
+ # Build MetaStyle
+ mkdir -p "$srcdir/tdesktop/Linux/DebugIntermediateStyle"
+ cd "$srcdir/tdesktop/Linux/DebugIntermediateStyle"
+ qmake CONFIG+=debug "../../Telegram/MetaStyle.pro"
+ make --silent
+
+ info_msg "Build MetaLang"
+ # Build MetaLang
+ mkdir -p "$srcdir/tdesktop/Linux/DebugIntermediateLang"
+ cd "$srcdir/tdesktop/Linux/DebugIntermediateLang"
+ qmake CONFIG+=debug "../../Telegram/MetaLang.pro"
+ make --silent
+
+ info_msg "Build Telegram Desktop"
+ # Build Telegram Desktop
+ mkdir -p "$srcdir/tdesktop/Linux/ReleaseIntermediate"
+ cd "$srcdir/tdesktop/Linux/ReleaseIntermediate"
+
+ qmake CONFIG+=release "../../Telegram/Telegram.pro"
+ local pattern="^PRE_TARGETDEPS +="
+ grep "$pattern" "$srcdir/tdesktop/Telegram/Telegram.pro" | sed "s/$pattern//g" | xargs make
+
+ qmake CONFIG+=release "../../Telegram/Telegram.pro"
+ make
+}
+
+check() {
+ local filePath="$srcdir/tdesktop/Linux/Release/Telegram"
+ if test -f "$filePath"; then
+ success_msg "Build successful done! :)"
+
+ local size;
+ size=$(stat -c %s "$filePath")
+ success_msg "File size of ${filePath}: ${size} Bytes"
+ else
+ error_msg "Build error, output file does not exist"
+ exit 1
+ fi
+}
+
+source ./.travis/common.sh
+
+run
diff --git a/.travis/check.sh b/.travis/check.sh
new file mode 100755
index 000000000..3fe7c7fd3
--- /dev/null
+++ b/.travis/check.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# Checks commit message, ...
+
+run() {
+ checkCommitMessage
+}
+
+checkCommitMessage() {
+ info_msg "Commit message: ${TRAVIS_COMMIT_MSG}";
+ info_msg "Is pull request: ${TRAVIS_PULL_REQUEST}";
+
+ if [[ $TRAVIS_PULL_REQUEST != "false" ]];then
+ if [[ $TRAVIS_COMMIT_MSG != *"Signed-off-by: "* ]];then
+ error_msg "The commit message does not contain the signature!"
+ error_msg "More information: https://github.com/telegramdesktop/tdesktop/blob/master/.github/CONTRIBUTING.md#sign-your-work"
+ exit 1
+ else
+ success_msg "Commit message contains signature"
+ fi
+ fi
+}
+
+source ./.travis/common.sh
+
+run
diff --git a/.travis/common.sh b/.travis/common.sh
new file mode 100755
index 000000000..dd3bb1967
--- /dev/null
+++ b/.travis/common.sh
@@ -0,0 +1,40 @@
+# set colors
+RCol='\e[0m' # Text Reset
+
+# Regular Bold Underline High Intensity BoldHigh Intens Background High Intensity Backgrounds
+Bla='\e[0;30m'; BBla='\e[1;30m'; UBla='\e[4;30m'; IBla='\e[0;90m'; BIBla='\e[1;90m'; On_Bla='\e[40m'; On_IBla='\e[0;100m';
+Red='\e[0;31m'; BRed='\e[1;31m'; URed='\e[4;31m'; IRed='\e[0;91m'; BIRed='\e[1;91m'; On_Red='\e[41m'; On_IRed='\e[0;101m';
+Gre='\e[0;32m'; BGre='\e[1;32m'; UGre='\e[4;32m'; IGre='\e[0;92m'; BIGre='\e[1;92m'; On_Gre='\e[42m'; On_IGre='\e[0;102m';
+Yel='\e[0;33m'; BYel='\e[1;33m'; UYel='\e[4;33m'; IYel='\e[0;93m'; BIYel='\e[1;93m'; On_Yel='\e[43m'; On_IYel='\e[0;103m';
+Blu='\e[0;34m'; BBlu='\e[1;34m'; UBlu='\e[4;34m'; IBlu='\e[0;94m'; BIBlu='\e[1;94m'; On_Blu='\e[44m'; On_IBlu='\e[0;104m';
+Pur='\e[0;35m'; BPur='\e[1;35m'; UPur='\e[4;35m'; IPur='\e[0;95m'; BIPur='\e[1;95m'; On_Pur='\e[45m'; On_IPur='\e[0;105m';
+Cya='\e[0;36m'; BCya='\e[1;36m'; UCya='\e[4;36m'; ICya='\e[0;96m'; BICya='\e[1;96m'; On_Cya='\e[46m'; On_ICya='\e[0;106m';
+Whi='\e[0;37m'; BWhi='\e[1;37m'; UWhi='\e[4;37m'; IWhi='\e[0;97m'; BIWhi='\e[1;97m'; On_Whi='\e[47m'; On_IWhi='\e[0;107m';
+
+# Set variables
+_qtver=5.5.1
+srcdir=${PWD}
+
+start_msg() {
+ echo -e "\n${Gre}$*${RCol}"
+}
+
+info_msg() {
+ echo -e "\n${Cya}$*${RCol}"
+}
+
+error_msg() {
+ echo -e "\n${BRed}$*${RCol}"
+}
+
+success_msg() {
+ echo -e "\n${BGre}$*${RCol}"
+}
+
+travis_fold_start() {
+ echo "travis_fold:start:$*"
+}
+
+travis_fold_end() {
+ echo "travis_fold:end:$*"
+}
diff --git a/README.md b/README.md
index 8d7c28ddf..32398eb24 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
This is the complete source code and the build instructions for the alpha version of the official desktop client for the [Telegram][telegram] messenger, based on the [Telegram API][telegram_api] and the [MTProto][telegram_proto] secure protocol.
+[](https://travis-ci.org/telegramdesktop/tdesktop)
+
The source code is published under GPLv3 with OpenSSL exception, the license is available [here][license].
## Supported systems
diff --git a/Telegram.sln b/Telegram.sln
index 836d4a043..6487c468a 100644
--- a/Telegram.sln
+++ b/Telegram.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.30501.0
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Telegram", "Telegram\Telegram.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}"
ProjectSection(ProjectDependencies) = postProject
@@ -18,10 +18,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Telegram\Updater
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MetaLang", "Telegram\MetaLang.vcxproj", "{E417CAA4-259B-4C99-88E3-805F1300E8EB}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F863EAD-33C9-4014-A573-93F085BA9CB1}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "codegen", "codegen", "{2F863EAD-33C9-4014-A573-93F085BA9CB1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Packer", "Telegram\Packer.vcxproj", "{56A9A4B2-21E5-4360-AFA8-85B43AC43B08}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "codegen_style", "Telegram\build\vc\codegen_style\codegen_style.vcxproj", "{E4DF8176-4DEF-4859-962F-B497E3E7A323}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -82,8 +84,21 @@ Global
{56A9A4B2-21E5-4360-AFA8-85B43AC43B08}.Deploy|x64.ActiveCfg = Release|Win32
{56A9A4B2-21E5-4360-AFA8-85B43AC43B08}.Release|Win32.ActiveCfg = Release|Win32
{56A9A4B2-21E5-4360-AFA8-85B43AC43B08}.Release|x64.ActiveCfg = Release|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Debug|Win32.Build.0 = Debug|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Debug|x64.ActiveCfg = Debug|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Deploy|Win32.ActiveCfg = Debug|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Deploy|Win32.Build.0 = Debug|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Deploy|x64.ActiveCfg = Release|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Deploy|x64.Build.0 = Release|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Release|Win32.ActiveCfg = Release|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Release|Win32.Build.0 = Release|Win32
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323}.Release|x64.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {E4DF8176-4DEF-4859-962F-B497E3E7A323} = {2F863EAD-33C9-4014-A573-93F085BA9CB1}
+ EndGlobalSection
EndGlobal
diff --git a/Telegram/Build.bat b/Telegram/Build.bat
index 788f9c766..fcfe23316 100644
--- a/Telegram/Build.bat
+++ b/Telegram/Build.bat
@@ -56,7 +56,7 @@ if %BetaVersion% neq 0 (
exit /b 1
)
)
-cd SourceFiles\
+cd Resources\
if "%1" == "fast" (
echo Skipping touching of telegram.qrc..
) else (
diff --git a/Telegram/Build.sh b/Telegram/Build.sh
index 650e3692b..cf02941e0 100755
--- a/Telegram/Build.sh
+++ b/Telegram/Build.sh
@@ -192,7 +192,7 @@ if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ] || [ "$BuildTarg
DropboxSymbolsPath="./../../../Dropbox/Telegram/symbols"
if [ "$FastParam" != "fast" ]; then
- touch "./SourceFiles/telegram.qrc"
+ touch "./Resources/telegram.qrc"
fi
xcodebuild -project Telegram.xcodeproj -alltargets -configuration Release build
diff --git a/Telegram/MetaLang.pro b/Telegram/MetaLang.pro
index dbef70ae9..0dfa29a6f 100644
--- a/Telegram/MetaLang.pro
+++ b/Telegram/MetaLang.pro
@@ -12,7 +12,7 @@ CONFIG(release, debug|release) {
DESTDIR = ./../ReleaseLang
}
-CONFIG += plugin static
+CONFIG += plugin static c++11
macx {
QMAKE_INFO_PLIST = ./SourceFiles/_other/Lang.plist
diff --git a/Telegram/MetaStyle.pro b/Telegram/MetaStyle.pro
index 2ef67ea2e..a68a44385 100644
--- a/Telegram/MetaStyle.pro
+++ b/Telegram/MetaStyle.pro
@@ -12,7 +12,7 @@ CONFIG(release, debug|release) {
DESTDIR = ./../ReleaseStyle
}
-CONFIG += plugin static
+CONFIG += plugin static c++11
macx {
QMAKE_INFO_PLIST = ./SourceFiles/_other/Style.plist
diff --git a/Telegram/Resources/LangList b/Telegram/Resources/LangList
new file mode 100644
index 000000000..658122760
--- /dev/null
+++ b/Telegram/Resources/LangList
@@ -0,0 +1 @@
+de,es,it,ko,nl,pt_BR
diff --git a/Telegram/SourceFiles/art/bg.jpg b/Telegram/Resources/art/bg.jpg
similarity index 100%
rename from Telegram/SourceFiles/art/bg.jpg
rename to Telegram/Resources/art/bg.jpg
diff --git a/Telegram/SourceFiles/art/bg0.png b/Telegram/Resources/art/bg0.png
similarity index 100%
rename from Telegram/SourceFiles/art/bg0.png
rename to Telegram/Resources/art/bg0.png
diff --git a/Telegram/SourceFiles/art/blank.gif b/Telegram/Resources/art/blank.gif
similarity index 100%
rename from Telegram/SourceFiles/art/blank.gif
rename to Telegram/Resources/art/blank.gif
diff --git a/Telegram/SourceFiles/art/channelcolor1.png b/Telegram/Resources/art/channelcolor1.png
similarity index 100%
rename from Telegram/SourceFiles/art/channelcolor1.png
rename to Telegram/Resources/art/channelcolor1.png
diff --git a/Telegram/SourceFiles/art/channelcolor2.png b/Telegram/Resources/art/channelcolor2.png
similarity index 100%
rename from Telegram/SourceFiles/art/channelcolor2.png
rename to Telegram/Resources/art/channelcolor2.png
diff --git a/Telegram/SourceFiles/art/channelcolor3.png b/Telegram/Resources/art/channelcolor3.png
similarity index 100%
rename from Telegram/SourceFiles/art/channelcolor3.png
rename to Telegram/Resources/art/channelcolor3.png
diff --git a/Telegram/SourceFiles/art/channelcolor4.png b/Telegram/Resources/art/channelcolor4.png
similarity index 100%
rename from Telegram/SourceFiles/art/channelcolor4.png
rename to Telegram/Resources/art/channelcolor4.png
diff --git a/Telegram/SourceFiles/art/chatcolor1.png b/Telegram/Resources/art/chatcolor1.png
similarity index 100%
rename from Telegram/SourceFiles/art/chatcolor1.png
rename to Telegram/Resources/art/chatcolor1.png
diff --git a/Telegram/SourceFiles/art/chatcolor2.png b/Telegram/Resources/art/chatcolor2.png
similarity index 100%
rename from Telegram/SourceFiles/art/chatcolor2.png
rename to Telegram/Resources/art/chatcolor2.png
diff --git a/Telegram/SourceFiles/art/chatcolor3.png b/Telegram/Resources/art/chatcolor3.png
similarity index 100%
rename from Telegram/SourceFiles/art/chatcolor3.png
rename to Telegram/Resources/art/chatcolor3.png
diff --git a/Telegram/SourceFiles/art/chatcolor4.png b/Telegram/Resources/art/chatcolor4.png
similarity index 100%
rename from Telegram/SourceFiles/art/chatcolor4.png
rename to Telegram/Resources/art/chatcolor4.png
diff --git a/Telegram/SourceFiles/art/emoji.webp b/Telegram/Resources/art/emoji.webp
similarity index 100%
rename from Telegram/SourceFiles/art/emoji.webp
rename to Telegram/Resources/art/emoji.webp
diff --git a/Telegram/SourceFiles/art/emoji_125x.webp b/Telegram/Resources/art/emoji_125x.webp
similarity index 100%
rename from Telegram/SourceFiles/art/emoji_125x.webp
rename to Telegram/Resources/art/emoji_125x.webp
diff --git a/Telegram/SourceFiles/art/emoji_150x.webp b/Telegram/Resources/art/emoji_150x.webp
similarity index 100%
rename from Telegram/SourceFiles/art/emoji_150x.webp
rename to Telegram/Resources/art/emoji_150x.webp
diff --git a/Telegram/SourceFiles/art/emoji_200x.webp b/Telegram/Resources/art/emoji_200x.webp
similarity index 100%
rename from Telegram/SourceFiles/art/emoji_200x.webp
rename to Telegram/Resources/art/emoji_200x.webp
diff --git a/Telegram/SourceFiles/art/emoji_250x.webp b/Telegram/Resources/art/emoji_250x.webp
similarity index 100%
rename from Telegram/SourceFiles/art/emoji_250x.webp
rename to Telegram/Resources/art/emoji_250x.webp
diff --git a/Telegram/SourceFiles/art/favicon.ico b/Telegram/Resources/art/favicon.ico
similarity index 100%
rename from Telegram/SourceFiles/art/favicon.ico
rename to Telegram/Resources/art/favicon.ico
diff --git a/Telegram/SourceFiles/art/fonts/OpenSans-Bold.ttf b/Telegram/Resources/art/fonts/OpenSans-Bold.ttf
similarity index 100%
rename from Telegram/SourceFiles/art/fonts/OpenSans-Bold.ttf
rename to Telegram/Resources/art/fonts/OpenSans-Bold.ttf
diff --git a/Telegram/SourceFiles/art/fonts/OpenSans-Regular.ttf b/Telegram/Resources/art/fonts/OpenSans-Regular.ttf
similarity index 100%
rename from Telegram/SourceFiles/art/fonts/OpenSans-Regular.ttf
rename to Telegram/Resources/art/fonts/OpenSans-Regular.ttf
diff --git a/Telegram/SourceFiles/art/fonts/OpenSans-Semibold.ttf b/Telegram/Resources/art/fonts/OpenSans-Semibold.ttf
similarity index 100%
rename from Telegram/SourceFiles/art/fonts/OpenSans-Semibold.ttf
rename to Telegram/Resources/art/fonts/OpenSans-Semibold.ttf
diff --git a/Telegram/SourceFiles/art/icon128.png b/Telegram/Resources/art/icon128.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon128.png
rename to Telegram/Resources/art/icon128.png
diff --git a/Telegram/SourceFiles/art/icon128@2x.png b/Telegram/Resources/art/icon128@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon128@2x.png
rename to Telegram/Resources/art/icon128@2x.png
diff --git a/Telegram/SourceFiles/art/icon16.png b/Telegram/Resources/art/icon16.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon16.png
rename to Telegram/Resources/art/icon16.png
diff --git a/Telegram/SourceFiles/art/icon16@2x.png b/Telegram/Resources/art/icon16@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon16@2x.png
rename to Telegram/Resources/art/icon16@2x.png
diff --git a/Telegram/SourceFiles/art/icon256.ico b/Telegram/Resources/art/icon256.ico
similarity index 100%
rename from Telegram/SourceFiles/art/icon256.ico
rename to Telegram/Resources/art/icon256.ico
diff --git a/Telegram/SourceFiles/art/icon256.png b/Telegram/Resources/art/icon256.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon256.png
rename to Telegram/Resources/art/icon256.png
diff --git a/Telegram/SourceFiles/art/icon256@2x.png b/Telegram/Resources/art/icon256@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon256@2x.png
rename to Telegram/Resources/art/icon256@2x.png
diff --git a/Telegram/SourceFiles/art/icon32.png b/Telegram/Resources/art/icon32.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon32.png
rename to Telegram/Resources/art/icon32.png
diff --git a/Telegram/SourceFiles/art/icon32@2x.png b/Telegram/Resources/art/icon32@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon32@2x.png
rename to Telegram/Resources/art/icon32@2x.png
diff --git a/Telegram/SourceFiles/art/icon48.png b/Telegram/Resources/art/icon48.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon48.png
rename to Telegram/Resources/art/icon48.png
diff --git a/Telegram/SourceFiles/art/icon48@2x.png b/Telegram/Resources/art/icon48@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon48@2x.png
rename to Telegram/Resources/art/icon48@2x.png
diff --git a/Telegram/SourceFiles/art/icon512.png b/Telegram/Resources/art/icon512.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon512.png
rename to Telegram/Resources/art/icon512.png
diff --git a/Telegram/SourceFiles/art/icon512@2x.png b/Telegram/Resources/art/icon512@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon512@2x.png
rename to Telegram/Resources/art/icon512@2x.png
diff --git a/Telegram/SourceFiles/art/icon64.png b/Telegram/Resources/art/icon64.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon64.png
rename to Telegram/Resources/art/icon64.png
diff --git a/Telegram/SourceFiles/art/icon64@2x.png b/Telegram/Resources/art/icon64@2x.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon64@2x.png
rename to Telegram/Resources/art/icon64@2x.png
diff --git a/Telegram/SourceFiles/art/icon_green.png b/Telegram/Resources/art/icon_green.png
similarity index 100%
rename from Telegram/SourceFiles/art/icon_green.png
rename to Telegram/Resources/art/icon_green.png
diff --git a/Telegram/SourceFiles/art/iconbig256.png b/Telegram/Resources/art/iconbig256.png
similarity index 100%
rename from Telegram/SourceFiles/art/iconbig256.png
rename to Telegram/Resources/art/iconbig256.png
diff --git a/Telegram/SourceFiles/art/iconbig_green.png b/Telegram/Resources/art/iconbig_green.png
similarity index 100%
rename from Telegram/SourceFiles/art/iconbig_green.png
rename to Telegram/Resources/art/iconbig_green.png
diff --git a/Telegram/SourceFiles/art/newmsg.wav b/Telegram/Resources/art/newmsg.wav
similarity index 100%
rename from Telegram/SourceFiles/art/newmsg.wav
rename to Telegram/Resources/art/newmsg.wav
diff --git a/Telegram/SourceFiles/art/osxsetup.tif b/Telegram/Resources/art/osxsetup.tif
similarity index 100%
rename from Telegram/SourceFiles/art/osxsetup.tif
rename to Telegram/Resources/art/osxsetup.tif
diff --git a/Telegram/SourceFiles/art/osxsetup.tiff b/Telegram/Resources/art/osxsetup.tiff
similarity index 100%
rename from Telegram/SourceFiles/art/osxsetup.tiff
rename to Telegram/Resources/art/osxsetup.tiff
diff --git a/Telegram/SourceFiles/art/osxsetup@2x.tif b/Telegram/Resources/art/osxsetup@2x.tif
similarity index 100%
rename from Telegram/SourceFiles/art/osxsetup@2x.tif
rename to Telegram/Resources/art/osxsetup@2x.tif
diff --git a/Telegram/SourceFiles/art/osxtray.png b/Telegram/Resources/art/osxtray.png
similarity index 100%
rename from Telegram/SourceFiles/art/osxtray.png
rename to Telegram/Resources/art/osxtray.png
diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/Resources/art/sprite.png
similarity index 90%
rename from Telegram/SourceFiles/art/sprite.png
rename to Telegram/Resources/art/sprite.png
index 86e0fd4ec..b9c755857 100644
Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/Resources/art/sprite.png differ
diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/Resources/art/sprite_200x.png
similarity index 84%
rename from Telegram/SourceFiles/art/sprite_200x.png
rename to Telegram/Resources/art/sprite_200x.png
index 3c2c54606..964c993cf 100644
Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/Resources/art/sprite_200x.png differ
diff --git a/Telegram/SourceFiles/art/usercolor1.png b/Telegram/Resources/art/usercolor1.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor1.png
rename to Telegram/Resources/art/usercolor1.png
diff --git a/Telegram/SourceFiles/art/usercolor2.png b/Telegram/Resources/art/usercolor2.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor2.png
rename to Telegram/Resources/art/usercolor2.png
diff --git a/Telegram/SourceFiles/art/usercolor3.png b/Telegram/Resources/art/usercolor3.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor3.png
rename to Telegram/Resources/art/usercolor3.png
diff --git a/Telegram/SourceFiles/art/usercolor4.png b/Telegram/Resources/art/usercolor4.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor4.png
rename to Telegram/Resources/art/usercolor4.png
diff --git a/Telegram/SourceFiles/art/usercolor5.png b/Telegram/Resources/art/usercolor5.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor5.png
rename to Telegram/Resources/art/usercolor5.png
diff --git a/Telegram/SourceFiles/art/usercolor6.png b/Telegram/Resources/art/usercolor6.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor6.png
rename to Telegram/Resources/art/usercolor6.png
diff --git a/Telegram/SourceFiles/art/usercolor7.png b/Telegram/Resources/art/usercolor7.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor7.png
rename to Telegram/Resources/art/usercolor7.png
diff --git a/Telegram/SourceFiles/art/usercolor8.png b/Telegram/Resources/art/usercolor8.png
similarity index 100%
rename from Telegram/SourceFiles/art/usercolor8.png
rename to Telegram/Resources/art/usercolor8.png
diff --git a/Telegram/SourceFiles/etc/qt_linux.conf b/Telegram/Resources/etc/qt_linux.conf
similarity index 100%
rename from Telegram/SourceFiles/etc/qt_linux.conf
rename to Telegram/Resources/etc/qt_linux.conf
diff --git a/Telegram/SourceFiles/etc/qt_win.conf b/Telegram/Resources/etc/qt_win.conf
similarity index 100%
rename from Telegram/SourceFiles/etc/qt_win.conf
rename to Telegram/Resources/etc/qt_win.conf
diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings
index c5b317fdd..ac8ec11f8 100644
--- a/Telegram/Resources/lang.strings
+++ b/Telegram/Resources/lang.strings
@@ -399,7 +399,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_preview_loading" = "Getting Link Info...";
"lng_profile_chat_unaccessible" = "Group is unaccessible";
-"lng_topbar_info" = "Info";
"lng_profile_about_section" = "About";
"lng_profile_description_section" = "Description";
"lng_profile_settings_section" = "Settings";
@@ -601,6 +600,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "In reply to";
+"lng_bot_share_location_unavailable" = "Sorry, the location sharing is currently unavailable in Telegram Desktop.";
+"lng_bot_inline_geo_unavailable" = "Sorry, this bot requires location sharing.\nIt is not available in Telegram Desktop.";
+"lng_bot_share_phone" = "Share Phone Number?";
+"lng_bot_share_phone_confirm" = "Share";
+
"lng_attach_failed" = "Failed";
"lng_attach_file" = "File";
"lng_attach_photo" = "Photo";
@@ -685,8 +689,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_cant_invite_not_contact_channel" = "Sorry, you can only add mutual contacts\nto channels at the moment.\n{more_info}";
"lng_cant_more_info" = "More info »";
"lng_cant_invite_banned" = "Sorry, only admin can add this user.";
-"lng_cant_invite_privacy" = "Sorry, you cannot add this user to groups because of the privacy settings.";
-"lng_cant_invite_privacy_channel" = "Sorry, you cannot add this user to channels because of the privacy settings.";
+"lng_cant_invite_privacy" = "Sorry, you cannot add this user to groups because of their privacy settings.";
+"lng_cant_invite_privacy_channel" = "Sorry, you cannot add this user to channels because of their privacy settings.";
"lng_cant_do_this" = "Sorry, this action is unavailable.";
"lng_send_button" = "Send";
@@ -810,6 +814,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
"lng_forwarding_from" = "{user} and {count:_not_used_|# other|# others}";
"lng_forwarding_from_two" = "{user} and {second_user}";
+"lng_inline_switch_choose" = "Choose conversation...";
+"lng_inline_switch_cant" = "Sorry, no way to write here :(";
"lng_share_cant" = "Sorry, no way to share here :(";
"lng_reply_cant" = "Sorry, no way to reply to an old message in supergroup :(";
@@ -886,12 +892,16 @@ 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_minor" = "— Bug fixes and other minor improvements";
-"lng_new_version_text" = "PUBLIC GROUPS, PINNED POSTS, 5,000 MEMBERS\n\n— Groups can now have 5,000 members (up from 1,000)\n— Groups of any size may be converted to supergroups\n\nNew tools for supergroup admins:\n\n— Make your group public by setting up a public link – anyone will be able to view the chat and join it\n— Pin messages to keep important updates visible and notify all members\n— Select messages to delete, report as spam, block users, or remove all messages from a user\n\nMore about this update:\n{link}";
+"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_menu_insert_unicode" = "Insert Unicode control character";
"lng_full_name" = "{first_name} {last_name}";
+// Not used
+
+"lng_topbar_info" = "Info";
+
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";
diff --git a/Telegram/SourceFiles/langs/lang_de.strings b/Telegram/Resources/langs/lang_de.strings
similarity index 96%
rename from Telegram/SourceFiles/langs/lang_de.strings
rename to Telegram/Resources/langs/lang_de.strings
index a89dc41e8..e24075c48 100644
--- a/Telegram/SourceFiles/langs/lang_de.strings
+++ b/Telegram/Resources/langs/lang_de.strings
@@ -544,7 +544,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_action_pinned_media" = "{from} hat {media} angeheftet";
"lng_action_pinned_media_photo" = "ein Bild";
"lng_action_pinned_media_video" = "ein Video";
-"lng_action_pinned_media_audio" = "ein Audio";
+"lng_action_pinned_media_audio" = "eine Audiodatei";
"lng_action_pinned_media_voice" = "eine Sprachnachricht";
"lng_action_pinned_media_file" = "eine Datei";
"lng_action_pinned_media_gif" = "ein GIF";
@@ -601,6 +601,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Antwort auf";
+"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_share_phone" = "Telefonnummer teilen?";
+"lng_bot_share_phone_confirm" = "Teilen";
+
"lng_attach_failed" = "Uploadfehler";
"lng_attach_file" = "Datei";
"lng_attach_photo" = "Bild";
@@ -685,8 +690,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_cant_invite_not_contact_channel" = "Du kannst nur Personen hinzufügen,\nwenn ihr eure Nummern ausgetauscht habt.\n{more_info}";
"lng_cant_more_info" = "Weitere Infos »";
"lng_cant_invite_banned" = "Nur Admins können diesen Nutzer hinzufügen.";
-"lng_cant_invite_privacy" = "Du kannst mit diesen Nutzern keine Gruppe erstellen, weil sie es nicht erlauben.";
-"lng_cant_invite_privacy_channel" = "Du kannst diese Nutzer keinen Kanälen hinzufügen, weil sie es nicht erlauben.";
+"lng_cant_invite_privacy" = "Du kannst mit diesem Nutzer keine Gruppe erstellen, weil er es nicht erlaubt.";
+"lng_cant_invite_privacy_channel" = "Du kannst diesen Nutzer keinen Kanälen hinzufügen, weil er es nicht erlaubt.";
"lng_cant_do_this" = "Verzeihung. Das ist leider nicht möglich.";
"lng_send_button" = "Senden";
@@ -698,7 +703,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_will_be_notified" = "Mitglieder werden benachrichtigt";
"lng_wont_be_notified" = "Mitglieder werden nicht benachrichtigt";
"lng_empty_history" = "";
-"lng_willbe_history" = "Chat auswählen um zu schreiben";
+"lng_willbe_history" = "Chat auswählen, um zu schreiben";
"lng_message_with_from" = "[c]{from}:[/c] {message}";
"lng_from_you" = "Ich";
"lng_bot_description" = "Was kann dieser Bot?";
@@ -725,10 +730,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_user_action_record_video" = "{user} sendet ein Video";
"lng_send_action_upload_video" = "schickt ein Video";
"lng_user_action_upload_video" = "{user} sendet ein Video";
-"lng_send_action_record_audio" = "nimmt ein Audio auf";
-"lng_user_action_record_audio" = "{user} nimmt eine Sprachnachricht auf";
-"lng_send_action_upload_audio" = "sendet eine Sprachnachricht";
-"lng_user_action_upload_audio" = "{user} sendet eine Sprachnachricht";
+"lng_send_action_record_audio" = "nimmt Sprachnachricht auf";
+"lng_user_action_record_audio" = "{user} nimmt Sprachnachricht auf";
+"lng_send_action_upload_audio" = "sendet Sprachnachricht";
+"lng_user_action_upload_audio" = "{user} sendet Sprachnachricht";
"lng_send_action_upload_photo" = "sendet ein Bild";
"lng_user_action_upload_photo" = "{user} sendet ein Bild";
"lng_send_action_upload_file" = "sendet eine Datei";
@@ -810,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|Nachrichtenanhang|# Nachrichtenanhänge}";
"lng_forwarding_from" = "{user} und {count:_not_used_|# anderer|# andere}";
"lng_forwarding_from_two" = "{user} und {second_user}";
+"lng_inline_switch_choose" = "Chat wählen...";
+"lng_inline_switch_cant" = "Hier kannst du nicht schreiben :(";
"lng_share_cant" = "Weiterleitung nicht möglich :(";
"lng_reply_cant" = "Leider kann man alte Nachrichten in Supergruppen nicht beantworten :(";
@@ -886,7 +893,7 @@ 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_minor" = "— Fehlerbehebungen und Softwareoptimierungen";
-"lng_new_version_text" = "ÖFFENTLICHE GRUPPEN, NACHRICHTEN ANHEFTEN, 5000 MITGLIEDER\n\n— Gruppen dürfen nun 5.000 Mitglieder haben (zuvor waren es 1.000)\n— Jede Gruppe - egal wie groß - kann ab sofort in eine Supergruppe geändert werden\n\nNeue Werkzeuge für Supergruppen-Admins:\n\n— Mache deine Gruppe öffentlich: Jeder kann den Inhalt einsehen und sie betreten\n— Hefte Nachrichten an: Perfekt um Gruppenmitglieder über Neuigkeiten zu informieren\n— Lösche mehrere Nachrichten, melde sie aufgrund von Spam, blockiere Mitglieder oder entferne alle Nachrichten von bestimmten Nutzern\n\nMehr Infos zum neuen Update:\n{link}";
+"lng_new_version_text" = "BOTS 2.0\n\n— Bot API 2.0: Das größte Update unserer Bot-Plattform seit Juni 2015.\n— Bots können vorhandene Nachrichten aktualisieren, während du mit dem Bot schreibst.\n— Neue Inline-Tastaturen mit 'Callback', 'URL öffnen' oder 'in den Inline-Modus wechseln'-Knöpfen, für eine noch bessere Integration.\n— Inline-Bots dürfen nun alle erlaubten Anhänge senden (Videos, Musik, Sticker, Dateien, etc.).\n— Probiere doch mal einen der folgenden Beispielbots aus: @music, @sticker, @youtube, @foursquare\n\nMehr Infos: {link}";
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";
diff --git a/Telegram/SourceFiles/langs/lang_es.strings b/Telegram/Resources/langs/lang_es.strings
similarity index 97%
rename from Telegram/SourceFiles/langs/lang_es.strings
rename to Telegram/Resources/langs/lang_es.strings
index 79a8f1247..88da8efdc 100644
--- a/Telegram/SourceFiles/langs/lang_es.strings
+++ b/Telegram/Resources/langs/lang_es.strings
@@ -601,6 +601,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Respondiendo a";
+"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_share_phone" = "¿Compartir número de teléfono?";
+"lng_bot_share_phone_confirm" = "Compartir";
+
"lng_attach_failed" = "Fallido";
"lng_attach_file" = "Archivo";
"lng_attach_photo" = "Foto";
@@ -810,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|Mensaje adjunto|# mensajes adjuntos}";
"lng_forwarding_from" = "{user} y {count:_not_used_|# otro|# otros}";
"lng_forwarding_from_two" = "{user} y {second_user}";
+"lng_inline_switch_choose" = "Elige una conversación...";
+"lng_inline_switch_cant" = "Lo sentimos, no puedes escribir aquí :(";
"lng_share_cant" = "Lo sentimos. No hay forma de compartir aquí :(";
"lng_reply_cant" = "Lo sentimos, no puedes responder un mensaje viejo en un supergrupo :(";
@@ -886,7 +893,7 @@ 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_minor" = "— Corrección de errores y otras mejoras menores";
-"lng_new_version_text" = "GRUPOS PÚBLICOS, PUBLICACIONES ANCLADAS, 5000 MIEMBROS\n\n— Los grupos ahora pueden tener hasta 5000 miembros (antes eran 1000)\n— Los grupos de cualquier tamaño pueden ser convertidos en supergrupos\n\nNuevas herramientas para los administradores de supergrupos:\n\n— Haz público tu grupo, generando un enlace público. Cualquiera podrá ver el chat y unirse.\n— Ancla mensajes, para mantener visible lo importante, y notifica a todos los miembros.\n— Elige varios mensajes para eliminar, reportar como spam, bloquear usuarios o quitar todos los mensajes de un usuario en particular.\n\nMás sobre esta actualización:\n{link}";
+"lng_new_version_text" = "BOTS 2.0\n\n— Presentamos la API para bots 2.0, la actualización más grande en nuestra plataforma para bots desde junio de 2015.\n— Desde ahora, los bots pueden actualizar los mensajes existentes sobre la marcha, al interactuar con ellos.\n— Los nuevos teclados integrados con retrollamada, botones para ‘abrir URL’ o ‘cambiar al modo integrado’, ayudan a crear una interfaz fluida.\n— Ahora los bots integrados pueden enviar todos los adjuntos soportados en Telegram (vídeos, música, stickers, archivos, etc.).\n— Prueba estos ejemplos de bots para que veas lo que viene: @music, @sticker, @youtube, @foursquare\n\nMás en: {link}";
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";
diff --git a/Telegram/SourceFiles/langs/lang_it.strings b/Telegram/Resources/langs/lang_it.strings
similarity index 96%
rename from Telegram/SourceFiles/langs/lang_it.strings
rename to Telegram/Resources/langs/lang_it.strings
index 2dd317d7e..ebdce80d8 100644
--- a/Telegram/SourceFiles/langs/lang_it.strings
+++ b/Telegram/Resources/langs/lang_it.strings
@@ -138,7 +138,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_pinned_notify" = "Notifica tutti i membri";
"lng_intro" = "Benvenuti nell'app desktop ufficiale di [a href=\"https://telegram.org/\"]Telegram[/a].\nÈ [b]veloce[/b] e [b]sicura[/b].";
-"lng_start_msgs" = "INIZIA A MESSAGGIARE";
+"lng_start_msgs" = "INIZIA A CHATTARE";
"lng_intro_next" = "AVANTI";
"lng_intro_finish" = "ISCRIVITI";
@@ -379,7 +379,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_settings_reset_button" = "Chiudi";
"lng_settings_reset_done" = "Altre sessioni terminate";
"lng_settings_ask_question" = "Fai una domanda";
-"lng_settings_ask_sure" = "Per favore nota che il supporto di Telegram è fornito da volontari. Proviamo a rispondere quanto prima, ma potrebbe volerci del tempo.\n\nDai un'occhiata alle domande frequenti su Telegram: potrai trovare importanti suggerimenti riguardo alcune problematiche e risposte alla maggior parte delle domande.";
+"lng_settings_ask_sure" = "Per favore nota che il supporto di Telegram è fornito da volontari. Proviamo a rispondere quanto prima, ma potrebbe volerci del tempo.\n\nDai un'occhiata alle domande frequenti di Telegram: contengono suggerimenti importanti per risolvere i problemi e risposte a quasi tutte le domande.";
"lng_settings_faq_button" = "Domande frequenti";
"lng_settings_ask_ok" = "Chiedi";
"lng_settings_faq" = "Domande frequenti";
@@ -601,6 +601,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "In risposta a";
+"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_share_phone" = "Condividere il numero di telefono?";
+"lng_bot_share_phone_confirm" = "Condividi";
+
"lng_attach_failed" = "Fallito";
"lng_attach_file" = "File";
"lng_attach_photo" = "Foto";
@@ -685,8 +690,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_cant_invite_not_contact_channel" = "Spiacenti, ma al momento puoi aggiungere\nai canali solo contatti reciproci.\n{more_info}";
"lng_cant_more_info" = "Più info »";
"lng_cant_invite_banned" = "Spiacenti, solo l'amministratore può aggiungere questo utente.";
-"lng_cant_invite_privacy" = "Spiacenti, non puoi aggiungere questo utente al gruppo a causa delle sue impostazioni di privacy.";
-"lng_cant_invite_privacy_channel" = "Spiacenti, non puoi aggiungere questo utente al canale a causa delle sue impostazioni di privacy.";
+"lng_cant_invite_privacy" = "Spiacenti, non puoi aggiungere questo utente ai gruppi a causa delle sue impostazioni di privacy.";
+"lng_cant_invite_privacy_channel" = "Spiacenti, non puoi aggiungere questo utente ai canali a causa delle sue impostazioni di privacy.";
"lng_cant_do_this" = "Spiacenti, questa azione non è disponibile.";
"lng_send_button" = "Invia";
@@ -698,7 +703,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_will_be_notified" = "I post saranno notificati ai membri";
"lng_wont_be_notified" = "I post non saranno notificati ai membri";
"lng_empty_history" = "";
-"lng_willbe_history" = "Seleziona una chat per iniziare a messaggiare";
+"lng_willbe_history" = "Seleziona una chat per iniziare a chattare";
"lng_message_with_from" = "[c]{from}:[/c] {message}";
"lng_from_you" = "Tu";
"lng_bot_description" = "Cosa può fare questo bot?";
@@ -810,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|messaggio inoltrato|# messaggi inoltrati}";
"lng_forwarding_from" = "{user} e {count:_not_used_|# altro|altri #}";
"lng_forwarding_from_two" = "{user} e {second_user}";
+"lng_inline_switch_choose" = "Scegli conversazione...";
+"lng_inline_switch_cant" = "Spiacenti, impossibile scrivere qui :(";
"lng_share_cant" = "Spiacenti, impossibile condividere qui :(";
"lng_reply_cant" = "Spiacenti, non si può rispondere a un vecchio messaggio nel supergruppo :(";
@@ -886,7 +893,7 @@ 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_minor" = "— Risoluzione di problemi e altri miglioramenti minori";
-"lng_new_version_text" = "GRUPPI PUBBLICI, POST FISSATI, 5000 MEMBRI\n\n— I gruppi possono ora avere fino a 5000 membri (dai precedenti 1000)\n— Puoi convertire qualsiasi gruppo in supergruppo\n\nNuovi strumenti per gli amministratori dei supergruppi:\n\n— Rendi pubblico il tuo gruppo inserendo un link - chiunque sarà in grado di vedere la chat e unirsi\n— Fissa i messaggi per rendere gli aggiornamenti importanti visibili\n— Seleziona diversi messaggi per eliminarli, segnalarli, bloccare utenti ed eliminare i loro messaggi\n\nPiù info su questo aggiornamento:\n{link}";
+"lng_new_version_text" = "BOT 2.0,\n\n— Introdotta l'API dei Bot 2.0, il più grande aggiornamento della nostra piattaforma da Giugno 2015.\n— I bot possono ora aggiornare i messaggi esistenti quando interagisci con loro.\n— Nuove tastiere per i bot inline con pulsanti callback (per non far inviare nuovi messaggi al bot), 'apri URL' o 'passa a modalità inline' per creare un'interfaccia senza interruzioni.\n— I bot inline possono ora inviare qualsiasi tipo di allegato supportato su Telegram (video,musica,sticker,file,etc.).\n— Prova questi bot di esempio per vedere cosa è in arrivo a breve: @music, @sticker, @youtube, @foursquare\n\nPiù informazioni: {link}";
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";
diff --git a/Telegram/SourceFiles/langs/lang_ko.strings b/Telegram/Resources/langs/lang_ko.strings
similarity index 90%
rename from Telegram/SourceFiles/langs/lang_ko.strings
rename to Telegram/Resources/langs/lang_ko.strings
index ff30db37d..3385080a4 100644
--- a/Telegram/SourceFiles/langs/lang_ko.strings
+++ b/Telegram/Resources/langs/lang_ko.strings
@@ -86,8 +86,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_cancel" = "취소";
"lng_continue" = "계속";
"lng_close" = "닫기";
-"lng_connecting" = "Connecting...";
-"lng_reconnecting" = "Reconnect {count:now|in # s|in # s}...";
+"lng_connecting" = "연결중...";
+"lng_reconnecting" = "재연결중..{count:now|in # 초|in # 초}...";
"lng_reconnecting_try_now" = "다시 시도";
"lng_status_service_notifications" = "서비스 알림";
@@ -108,7 +108,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_status_lastseen_date" = "{date}에 마지막으로 접속";
"lng_status_lastseen_date_time" = "{date}일 {time}에 마지막으로 접속";
"lng_status_online" = "온라인";
-"lng_status_connecting" = "connecting...";
+"lng_status_connecting" = "연결중...";
"lng_chat_status_unaccessible" = "그룹 접근 불가";
"lng_chat_status_members" = "{count:맴버 없음|#명|#명}";
@@ -127,7 +127,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_edit_deleted" = "메시지는 삭제 되었습니다.";
"lng_edit_too_long" = "메시지 길이가 너무 깁니다.";
"lng_edit_message" = "메시지 수정";
-"lng_edit_message_text" = "New message text...";
+"lng_edit_message_text" = "새로운 메시지...";
"lng_deleted" = "알 수 없음";
"lng_deleted_message" = "삭제된 메시지";
"lng_pinned_message" = "고정된 메시지";
@@ -162,7 +162,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_code_telegram" = "[b]텔레그램[/b] 앱으로 부터 방금 수신받은,\n코드를 입력해주세요.";
"lng_code_no_telegram" = "코드를 SMS로 전송";
"lng_code_call" = "텔레그램이 {minutes}:{seconds}후에는 전화를 겁니다.";
-"lng_code_calling" = "Requesting a call from Telegram...";
+"lng_code_calling" = "텔레그램으로부터 전화 요청을 하고 있습니다...";
"lng_code_called" = "텔레그램이 회원님의 전화번호로 전화를 걸었습니다.";
"lng_bad_phone" = "잘못된 전화번호입니다. 다시 시도해주세요.";
@@ -201,7 +201,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_dlg_new_channel_name" = "채널명";
"lng_no_contacts" = "연락처가 없습니다.";
"lng_no_chats" = "대화시 대화방이 존재 할 곳입니다.";
-"lng_contacts_loading" = "Loading...";
+"lng_contacts_loading" = "로딩중...";
"lng_contacts_not_found" = "연락처를 찾을 수 없음";
"lng_dlg_search_chat" = "이 채팅에서 검색";
"lng_dlg_search_channel" = "이 채널방에서 검색";
@@ -210,7 +210,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_settings_save" = "저장";
"lng_settings_upload" = "프로필 이미지 선택";
"lng_settings_crop_profile" = "프로필 사진으로 사용할 사각영역을 선택하세요";
-"lng_settings_uploading_photo" = "Uploading photo...";
+"lng_settings_uploading_photo" = "사진 업로드 중...";
"lng_username_title" = "아이디";
"lng_username_about" = "텔레그램 아이디를 설정할 수 있습니다. \n아이디를 설정하면 회원님의 전화번호를 몰라도 아이디로 회원님을 찾아 대화를 나눌 수 있습니다.\n아이디는 영문, 밑줄, 숫자로 a-z, _, 0-9, \n다섯 글자 이상으로 설정해 주세요.";
@@ -247,9 +247,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_settings_auto_update" = "자동 업데이트";
"lng_settings_current_version" = " {version}";
"lng_settings_check_now" = "업데이트 확인";
-"lng_settings_update_checking" = "Checking for updates...";
+"lng_settings_update_checking" = "업데이트 확인 중...";
"lng_settings_latest_installed" = "최신 버전이 설치되어 있습니다.";
-"lng_settings_downloading" = "Downloading update {ready} / {total} MB...";
+"lng_settings_downloading" = "데이트를 다운로드 중 {ready} / {total} MB..";
"lng_settings_update_ready" = "새로운 버전을 설치 할 수 있습니다.";
"lng_settings_update_now" = "재시작 합니다.";
"lng_settings_update_fail" = "업데이트 확인 실패 :(";
@@ -291,7 +291,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_download_path_failed" = "파일 다운로드를 시작 할 수 없습니다. 올바르지 않은 다운로드 경로가 원인 일 수도 있습니다.\n\n설정에 가시면 다운로드 경로를 변경하실 수 있습니다.";
"lng_download_path_settings" = "설정";
"lng_download_finish_failed" = "파일 다운로드를 끝낼 수 없습니다.\n\n다시 시도하시겠습니까?";
-"lng_download_path_clearing" = "Clearing...";
+"lng_download_path_clearing" = "초기화 중..";
"lng_download_path_cleared" = "초기화 완료!";
"lng_download_path_clear_failed" = "초기화 실패 :(";
@@ -300,7 +300,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_settings_images_cached" = "{count:_not_used_|이미지 #개|이미지 #개}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|음성 메시지 #개|음성 메시지 #개}, {size}";
"lng_local_storage_clear" = "전체 정리";
-"lng_local_storage_clearing" = "Clearing...";
+"lng_local_storage_clearing" = "초기화 중..";
"lng_local_storage_cleared" = "초기화 완료!";
"lng_local_storage_clear_failed" = "초기화 실패 :(";
@@ -331,7 +331,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_passcode_logout" = "로그아웃";
"lng_passcode_need_unblock" = "잠금코드를 먼저 해제해주세요.";
-"lng_cloud_password_waiting" = "Confirmation link sent to {email}...";
+"lng_cloud_password_waiting" = "{email}로 확인 이메일을 전송하였습니다..";
"lng_cloud_password_change" = "클라우드 비밀번호 변경";
"lng_cloud_password_create" = "클라우드 비밀번호";
"lng_cloud_password_remove" = "클라우드 비밀번호 삭제";
@@ -358,9 +358,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_cloud_password_is_same" = "비밀번호가 변경되지 않았습니다.";
"lng_connection_type" = "연결 유형:";
-"lng_connection_auto_connecting" = "Default (connecting...)";
+"lng_connection_auto_connecting" = "기본값 (연결중...)";
"lng_connection_auto" = "기본값 ({transport} 사용)";
-"lng_connection_proxy_connecting" = "Connecting through proxy...";
+"lng_connection_proxy_connecting" = "프록시 연결 중...";
"lng_connection_proxy" = "{transport} 프록시 연결";
"lng_connection_header" = "연결 유형";
"lng_connection_auto_rb" = "자동 (사용 가능하다면 TCP 아니면 HTTP 사용)";
@@ -396,7 +396,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_sessions_other_desc" = "동일한 휴대번호로 다른 휴대기기, 태블릿과 데스크탑에서 텔레그램 로그인이 가능합니다. 모든 데이터는 즉시 동기화 됩니다.";
"lng_sessions_terminate_all" = "다른 모든 세션 강제 종료";
-"lng_preview_loading" = "Getting Link Info...";
+"lng_preview_loading" = "링크 정보를 가져오는 중..";
"lng_profile_chat_unaccessible" = "그룹에 접근할 수 없습니다.";
"lng_topbar_info" = "정보";
@@ -439,7 +439,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_profile_sure_kick" = "{user}를 추방하시겠습니까?";
"lng_profile_sure_kick_channel" = "{user}를 추방하시겠습니까?";
"lng_profile_sure_kick_admin" = "{user}를 관리자에서 제외 하시겠습니까?";
-"lng_profile_loading" = "Loading...";
+"lng_profile_loading" = "로드중..";
"lng_profile_shared_media" = "공유된 미디어";
"lng_profile_no_media" = "대화에 미디어가 존재하지 않습니다.";
"lng_profile_photos" = "{count:_not_used_|#개의 사진|#개의 사진} »";
@@ -455,7 +455,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_profile_shared_links" = "{count:_not_used_|# 공유된 링크|# 공유된 링크} »";
"lng_profile_shared_links_header" = "공유된 링크 현황";
"lng_profile_copy_phone" = "전화번호 복사";
-"lng_profile_copy_fullname" = "Copy name";
+"lng_profile_copy_fullname" = "이름 복사";
"lng_channel_add_admins" = "새로운 관리자";
"lng_channel_add_members" = "구성원 추가";
@@ -601,6 +601,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "다음 유저에게 답장 :";
+"lng_bot_share_location_unavailable" = "죄송합니다, 위치 공유는 텔레그램 테스크탑에서는 현재 지원을 하고 있지 않습니다.";
+"lng_bot_inline_geo_unavailable" = "죄송합니다, 위치 공유는 텔레그램 테스크탑에서는 현재 지원을 하고 있지 않습니다.";
+"lng_bot_share_phone" = "전화번호를 공유하겠습니까?";
+"lng_bot_share_phone_confirm" = "공유";
+
"lng_attach_failed" = "실패";
"lng_attach_file" = "파일";
"lng_attach_photo" = "사진";
@@ -660,13 +665,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_stickers_remove" = "삭제";
"lng_stickers_return" = "실행취소";
"lng_stickers_restore" = "복구";
-"lng_stickers_count" = "{count:Loading...|# sticker|# stickers}";
+"lng_stickers_count" = "{count:Loading...|# 스티커|# 스티커}";
"lng_in_dlg_photo" = "사진";
-"lng_in_dlg_video" = "Video file";
-"lng_in_dlg_audio_file" = "Audio file";
+"lng_in_dlg_video" = "비디오 파일";
+"lng_in_dlg_audio_file" = "음성 파일";
"lng_in_dlg_contact" = "연락처";
-"lng_in_dlg_audio" = "Voice message";
+"lng_in_dlg_audio" = "음성 메시지";
"lng_in_dlg_file" = "파일";
"lng_in_dlg_sticker" = "스티커";
"lng_in_dlg_sticker_emoji" = "{emoji} (스티커)";
@@ -680,20 +685,20 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_report_spam_sure_group" = "선택한 그룹메시지를 스팸으로 신고하시겠습니까?";
"lng_report_spam_sure_channel" = "선택한 채널메시지를 스팸으로 신고하시겠습니까?";
"lng_report_spam_ok" = "신고하기";
-"lng_cant_send_to_not_contact" = "Sorry, you can only send messages to\nmutual contacts at the moment.\n{more_info}";
-"lng_cant_invite_not_contact" = "Sorry, you can only add mutual contacts\nto groups at the moment.\n{more_info}";
-"lng_cant_invite_not_contact_channel" = "Sorry, you can only add mutual contacts\nto channels at the moment.\n{more_info}";
+"lng_cant_send_to_not_contact" = "죄송하지만, 현재 서로 연락처가 추가된 \n회원들끼리만 전송이 가능합니다. \n{more_info}";
+"lng_cant_invite_not_contact" = "죄송하지만, 현재 그룹방에 서로 연락처가 추가된 \n회원들끼리만 추가 가능합니다. {more_info}";
+"lng_cant_invite_not_contact_channel" = "죄송하지만, 현재 채널방에 서로 연락처가 추가된 \n회원들끼리만 추가 가능합니다. \n{more_info}";
"lng_cant_more_info" = "자세한 정보 »";
-"lng_cant_invite_banned" = "Sorry, only admin can add this user.";
+"lng_cant_invite_banned" = "죄송하지만, 관리자만 회원 추가가 가능합니다.";
"lng_cant_invite_privacy" = "죄송합니다, 개인설정으로 인하여 이 사용자를 그룹에 초대할 수 없습니다.";
"lng_cant_invite_privacy_channel" = "죄송합니다, 개인설정으로 인하여 이 사용자를 채널에 초대할 수 없습니다.";
-"lng_cant_do_this" = "Sorry, this action is unavailable.";
+"lng_cant_do_this" = "죄송하지만, 할 수 없는 기능입니다.";
"lng_send_button" = "보내기";
-"lng_message_ph" = "Write a message...";
-"lng_comment_ph" = "Write a comment...";
-"lng_broadcast_ph" = "Broadcast a message...";
-"lng_broadcast_silent_ph" = "Silent broadcast...";
+"lng_message_ph" = "메시지 쓰기..";
+"lng_comment_ph" = "코멘트 쓰기...";
+"lng_broadcast_ph" = "단체메시지 쓰기...";
+"lng_broadcast_silent_ph" = "음소거 메시지...";
"lng_record_cancel" = "이 영역 밖에서 마우스 클릭을 해제하시면 취소가 됩니다.";
"lng_will_be_notified" = "메시지 작성시 구성원들에게 알림이 갑니다.";
"lng_wont_be_notified" = "메시지 작성시 구성원들에게 알림이 가지 않습니다.";
@@ -721,29 +726,29 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_user_typing" = "{user}님이 입력중입니다.";
"lng_users_typing" = "{user}님과 {second_user}님이 입력중입니다.";
"lng_many_typing" = "{count:_not_used_|#명이|#명이} 입력중입니다";
-"lng_send_action_record_video" = "recording a video";
-"lng_user_action_record_video" = "{user} is recording a video";
-"lng_send_action_upload_video" = "sending a video";
-"lng_user_action_upload_video" = "{user} is sending a video";
-"lng_send_action_record_audio" = "recording a voice message";
-"lng_user_action_record_audio" = "{user} is recording a voice message";
-"lng_send_action_upload_audio" = "sending a voice message";
-"lng_user_action_upload_audio" = "{user} is sending a voice message";
-"lng_send_action_upload_photo" = "sending a photo";
-"lng_user_action_upload_photo" = "{user} is sending a photo";
-"lng_send_action_upload_file" = "sending a file";
-"lng_user_action_upload_file" = "{user} is sending a file";
-"lng_send_action_geo_location" = "picking a location";
-"lng_user_action_geo_location" = "{user} is picking a location";
-"lng_send_action_choose_contact" = "choosing a contact";
-"lng_user_action_choose_contact" = "{user} is choosing a contact";
+"lng_send_action_record_video" = "비디오 녹화 중";
+"lng_user_action_record_video" = "{user}님이 녹화중입니다";
+"lng_send_action_upload_video" = "비디오 전송 중";
+"lng_user_action_upload_video" = "{user}님이 비디오를 전송 중입니다.";
+"lng_send_action_record_audio" = "음송 메시지 녹음 중";
+"lng_user_action_record_audio" = "{user}님이 오디오를 녹음 중입니다";
+"lng_send_action_upload_audio" = "음성 메시지 전송 중";
+"lng_user_action_upload_audio" = "{user}님이 음성 메시지는 전송 중입니다";
+"lng_send_action_upload_photo" = "사진 전송 중";
+"lng_user_action_upload_photo" = "{user}님이 사진을 전송 중입니다";
+"lng_send_action_upload_file" = "파일 전송 중";
+"lng_user_action_upload_file" = "{user}님이 파일을 전송 중입니다";
+"lng_send_action_geo_location" = "위치 선택 중";
+"lng_user_action_geo_location" = "{user}님이 위치를 선택 중입닏";
+"lng_send_action_choose_contact" = "연락처 선택 중";
+"lng_user_action_choose_contact" = "{user}님이 연락처를 선택 중입니다";
"lng_unread_bar" = "{count:_not_used_|#개의 읽지 않은 메시지|#개의 읽지 않은 메시지}";
"lng_maps_point" = "위치";
"lng_save_photo" = "사진 저장";
-"lng_save_video" = "Save video file";
-"lng_save_audio_file" = "Save audio file";
-"lng_save_audio" = "Save voice message";
+"lng_save_video" = "동영상 저장";
+"lng_save_audio_file" = "음성파일 저장";
+"lng_save_audio" = "음성 메시지 저장";
"lng_save_file" = "파일 저장";
"lng_save_downloaded" = "{ready} / {total} {mb}";
"lng_duration_and_size" = "{duration}, {size}";
@@ -760,7 +765,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_context_copy_email" = "이메일 복사";
"lng_context_copy_hashtag" = "해시태그 복사";
"lng_context_copy_mention" = "아이디 복사";
-"lng_context_save_image" = "Save Image As...";
+"lng_context_save_image" = "이미지를 다른 이름으로 저장..";
"lng_context_forward_image" = "이미지 전달";
"lng_context_delete_image" = "이미지 삭제";
"lng_context_copy_image" = "이미지 복사";
@@ -768,12 +773,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_context_cancel_download" = "다운로드 취소";
"lng_context_show_in_folder" = "탐색기에서 보기";
"lng_context_show_in_finder" = "탐색기에서 보기";
-"lng_context_save_video" = "Save Video File As...";
-"lng_context_save_audio_file" = "Save Audio File As...";
-"lng_context_save_audio" = "Save Voice Message As...";
+"lng_context_save_video" = "비디오를 다른 이름으로 저장...";
+"lng_context_save_audio_file" = "음성파일을 다른 이름으로 저장...";
+"lng_context_save_audio" = "음성메시지를 다른 이름으로 저장...";
"lng_context_pack_info" = "팩 정보";
"lng_context_pack_add" = "스티커 추가";
-"lng_context_save_file" = "Save File As...";
+"lng_context_save_file" = "파일을 다른 이름으로 저장...";
"lng_context_forward_file" = "파일 전달";
"lng_context_delete_file" = "파일 삭제";
"lng_context_close_file" = "파일 닫기";
@@ -800,7 +805,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_send_image_too_large" = "파일이 1.5GB 보다 큼으로 전송 할 수 없습니다 :(";
"lng_send_folder" = " «{name}»은 폴더이기 때문에 전송 할 수 없습니다 :(";
-"lng_forward_choose" = "Choose recipient...";
+"lng_forward_choose" = "수신자를 선택..";
"lng_forward_cant" = "이쪽으로 전달 할 수 없습니다 :(";
"lng_forward_confirm" = "{recipient} 님에게 전달하시겠습니까?";
"lng_forward_share_contact" = "{recipient} 님에게 연락처를 공유하시겠습니까?";
@@ -810,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|전달받은 메시지|# 개의 전달받은 메시지}";
"lng_forwarding_from" = "{user} 님과 {count:_not_used_|# 명|# 명}";
"lng_forwarding_from_two" = "{user} 님과 {second_user}";
+"lng_inline_switch_choose" = "대화 선택...";
+"lng_inline_switch_cant" = "죄송합니다. 이쪽으로 글을 쓸 수 없습니다 :(";
"lng_share_cant" = "이쪽으로 공유 할 수 없습니다 :(";
"lng_reply_cant" = "죄송합니다. 슈퍼그룹방의 이전 메시지에 답글을 할 수 없습니다 :(";
@@ -863,7 +870,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_search_global_results" = "아이디 검색 결과";
"lng_media_save_progress" = "{ready} / {total} {mb}";
-"lng_mediaview_save_as" = "Save As...";
+"lng_mediaview_save_as" = " 다른 이름으로 저장...";
"lng_mediaview_copy" = "복사하기";
"lng_mediaview_forward" = "전달";
"lng_mediaview_delete" = "삭제";
@@ -886,7 +893,7 @@ 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_minor" = "— 버그 수정 및 일부 기능 향상";
-"lng_new_version_text" = "공개 그룹, 메시지 고정, 5,000명\n\n— 그룹은 5,000명까지 가능 (기존 1,000명)\n— 모든 그룹은 구성원 크기에 상관 없이 슈퍼그룹으로 변환 가능\n\n슈퍼그룹 관리기능 추가:\n\n— 공개링크를 생성하여 그룹공개 가능 - 누구나 참여하여 대화가능\n— 메시지를 고정하여 중요한 내용을 표시하고 모두에게 알림\n— 여러 메시지를 선택하여 삭제, 스팸신고, 차단 혹은 특정 유저에게 메시지 삭제 가능\n\n자세한 사항:\n{link}";
+"lng_new_version_text" = "봇 API 2.0\n\n— 봇 API 2.0을 소개합니다, 2015년 6월 이후로 가장 큰 봇 플랫폼 업데이트입니다.\n— 봇을 활용하여 기존 송신한 메시지에 대한 업데이트가 가능합니다.\n— 새로운 Inline 키보드를 소개합니다, 콜백, URL열기 혹은 inline모드 전환 버튼으로 매끄러운 인터페이스를 도와줍니다.\n— Inline 봇은 텔레그램에서 활용 가능한 모든 첨부파일을 전송 할 수 있습니다. (비디오, 음악, 스티커, 파일등)\n— 샘플 봇을 활용하여 미리 업데이트 기능을 활용해보세요 : @music, @sticker, @youtoube, @foursquare\n\n봇에 대한 자세한 설명: {link}";
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";
diff --git a/Telegram/SourceFiles/langs/lang_nl.strings b/Telegram/Resources/langs/lang_nl.strings
similarity index 97%
rename from Telegram/SourceFiles/langs/lang_nl.strings
rename to Telegram/Resources/langs/lang_nl.strings
index 635693b07..ca8a27d0a 100644
--- a/Telegram/SourceFiles/langs/lang_nl.strings
+++ b/Telegram/Resources/langs/lang_nl.strings
@@ -601,6 +601,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Antwoord op";
+"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_share_phone" = "Telefoonnummer delen?";
+"lng_bot_share_phone_confirm" = "Delen";
+
"lng_attach_failed" = "Mislukt";
"lng_attach_file" = "Bestand";
"lng_attach_photo" = "Foto";
@@ -663,10 +668,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_stickers_count" = "{count:Laden..|# sticker|# stickers}";
"lng_in_dlg_photo" = "Foto";
-"lng_in_dlg_video" = "Video file";
-"lng_in_dlg_audio_file" = "Audio file";
+"lng_in_dlg_video" = "Video";
+"lng_in_dlg_audio_file" = "Audiobestand";
"lng_in_dlg_contact" = "Contact";
-"lng_in_dlg_audio" = "Voice message";
+"lng_in_dlg_audio" = "Spraakbericht";
"lng_in_dlg_file" = "Bestand";
"lng_in_dlg_sticker" = "Sticker";
"lng_in_dlg_sticker_emoji" = "{emoji} (sticker)";
@@ -741,9 +746,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_maps_point" = "Locatie";
"lng_save_photo" = "Afbeelding opslaan";
-"lng_save_video" = "Save video file";
-"lng_save_audio_file" = "Save audio file";
-"lng_save_audio" = "Save voice message";
+"lng_save_video" = "Video opslaan";
+"lng_save_audio_file" = "Audio opslaan";
+"lng_save_audio" = "Spraakbericht opslaan";
"lng_save_file" = "Bestand opslaan";
"lng_save_downloaded" = "{ready} / {total} {mb}";
"lng_duration_and_size" = "{duration}, {size}";
@@ -768,9 +773,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_context_cancel_download" = "Download annuleren";
"lng_context_show_in_folder" = "Weergeven in map";
"lng_context_show_in_finder" = "Weergeven in Finder";
-"lng_context_save_video" = "Save Video File As...";
-"lng_context_save_audio_file" = "Save Audio File As...";
-"lng_context_save_audio" = "Save Voice Message As...";
+"lng_context_save_video" = "Video opslaan als...";
+"lng_context_save_audio_file" = "Video opslaan als...";
+"lng_context_save_audio" = "Spraakbericht opslaan als...";
"lng_context_pack_info" = "Bundelinformatie";
"lng_context_pack_add" = "Stickers toevoegen";
"lng_context_save_file" = "Bestand opslaan als...";
@@ -810,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|Doorgestuurd bericht|# doorgestuurde berichten}";
"lng_forwarding_from" = "{user} en {count:_not_used_|# andere|# anderen}";
"lng_forwarding_from_two" = "{user} en {second_user}";
+"lng_inline_switch_choose" = "Kies chat...";
+"lng_inline_switch_cant" = "Sorry, je kunt hier niets delen :(";
"lng_share_cant" = "Sorry, delen hierheen kan niet :(";
"lng_reply_cant" = "Sorry, je kunt geen oude berichten beantwoorden in een supergroep :(";
@@ -886,7 +893,7 @@ 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_minor" = "— Probleemoplossing en andere kleine verbeteringen";
-"lng_new_version_text" = "PUBLIEKE GROEPEN, BERICHTEN VASTZETTEN, 5000 LEDEN\n\n— Ledenlimiet voor iedere groepsvorm opgehoogd naar 5000 (voorheen 1000).\n— Iedere groep met een willekeurig aantal leden kan nu worden opgewaardeerd naar een supergroep.\n\nNieuwe functies voor beheerders van supergroepen:\n\n— Maak je groep openbaar door een publieke link in te stellen - iedereen kan de chat zien en er lid van worden.\n— Zet berichten vast om belangrijke informatie weer te geven en alle leden te informeren.\n— Selecteer berichten om te verwijderen, ze als spam te melden, gebruikers te blokkeren of om alle berichten van bepaalde gebruikers ineens te verwijderen.\n\nMeer informatie over deze update:\n{link}";
+"lng_new_version_text" = "BOTS 2.0\n\n— Maak kennis met BOT API 2.0, onze grootste update voor het bot-platform sinds juni 2015.\n— Nieuwe Inline-keyboards met terugroep-functie, 'Open URL' of 'omschakelen naar inline'-knoppen voor een nog betere integratie.\n— Bots kunnen nu berichten bijwerken, direct tijdens je interactie met hen.\n— Inline-bots kunnen nu alle bijlagen sturen die worden ondersteund door Telegram (video's, muziek, stickers, bestanden, etc.).\n— Probeer deze voorbeeldbots uit voor een voorproefje: @music, @sticker, @youtube, @foursquare\n\nMeer over de nieuwe bots: {link}";
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";
diff --git a/Telegram/SourceFiles/langs/lang_pt_BR.strings b/Telegram/Resources/langs/lang_pt_BR.strings
similarity index 97%
rename from Telegram/SourceFiles/langs/lang_pt_BR.strings
rename to Telegram/Resources/langs/lang_pt_BR.strings
index 2113785ed..074af6ab9 100644
--- a/Telegram/SourceFiles/langs/lang_pt_BR.strings
+++ b/Telegram/Resources/langs/lang_pt_BR.strings
@@ -601,6 +601,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forwarded_signed" = "{channel} ({user})";
"lng_in_reply_to" = "Em resposta a";
+"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_share_phone" = "Compartilhar Número de Telefone?";
+"lng_bot_share_phone_confirm" = "Compartilhar";
+
"lng_attach_failed" = "Falhou";
"lng_attach_file" = "Arquivo";
"lng_attach_photo" = "Foto";
@@ -810,6 +815,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_forward_messages" = "{count:_not_used_|Mensagem encaminhada|# mensagens encaminhadas}";
"lng_forwarding_from" = "{user} e {count:_not_used_|# outro|# outros}";
"lng_forwarding_from_two" = "{user} e {second_user}";
+"lng_inline_switch_choose" = "Escolher conversa...";
+"lng_inline_switch_cant" = "Desculpe, não há como escrever aqui :(";
"lng_share_cant" = "Não há como compartilhar aqui :(";
"lng_reply_cant" = "Desculpe, não há como responder uma mensagem antiga no supergrupo :(";
@@ -886,7 +893,7 @@ 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_minor" = "— Resolução de bugs e outras melhorias menores";
-"lng_new_version_text" = "GRUPOS PÚBLICOS, POSTS FIXADOS, 5.000 MEMBROS\n\n— Grupos agora podem ter até 5.000 membros (mais que 1.000)\n— Grupos de qualquer tamanho podem ser convertidos a supergrupos\n\nNovas ferramentas para administradores dos supergrupos:\n\n— Torne seu grupo público configurando um link público - qualquer um poderá ver a conversa e entrar nela\n— Fixe mensagens para manter as atualizações mais importantes visíveis e notificar todos os membros\n— Selecione várias mensagens para apagar, reporte por spam, bloqueie usuários ou remova todas as mensagens de certos usuários\n\nMais sobre essa atualização:\n{link}";
+"lng_new_version_text" = "BOTS 2.0\n\n— Apresentamos a API para bots 2.0, a maior atualização de nossa plataforma para bots desde junho de 2015.\n— A partir de agora, os bots podem atualizar as mensagens existentes em tempo real, assim como interagir com elas.\n— Os novos teclados integrados com callback, botões para 'Abrir URL' ou 'Alterar para modo integrado', ajudam a criar uma interface fluída.\n— Os bots integrados agora podem enviar qualquer tipo de anexo suportado no Telegram (vídeos, músicas, stickers, arquivos, etc.).\n— Tente usar os bots de exemplos para ver o que está por vir: @music, @sticker, @youtube, @foursquare\n\nMais em: {link}";
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";
diff --git a/Telegram/SourceFiles/qmime/freedesktop.org.xml b/Telegram/Resources/qmime/freedesktop.org.xml
similarity index 100%
rename from Telegram/SourceFiles/qmime/freedesktop.org.xml
rename to Telegram/Resources/qmime/freedesktop.org.xml
diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt
index 5ada019a8..626846aac 100644
--- a/Telegram/Resources/style.txt
+++ b/Telegram/Resources/style.txt
@@ -886,6 +886,8 @@ dlgPaddingVer: 8px;
dlgHeight: 62px;
dlgPhotoPadding: 12px;
+dlgImportantHeight: 37px;
+
noContactsHeight: 100px;
noContactsFont: font(fsize);
noContactsColor: #777;
@@ -950,8 +952,9 @@ dlgUnreadColor: #FFF;
dlgUnreadBG: #6fc766;
dlgUnreadMutedBG: #bbb;
dlgUnreadFont: font(12px bold);
+dlgUnreadHeight: 19px;
+dlgUnreadTop: 1px;
dlgUnreadPaddingHor: 5px;
-dlgUnreadPaddingVer: 1px;
dlgUnreadRadius: 2px;
dlgBG: #FFF;
dlgHoverBG: #f5f5f5;
@@ -1052,8 +1055,8 @@ msgMinWidth: 190px;
msgPhotoSize: 33px;
msgPhotoSkip: 40px;
msgPadding: margins(13px, 7px, 13px, 8px);
-msgMargin: margins(13px, 6px, 53px, 2px);
-msgMarginTopAttached: 2px;
+msgMargin: margins(13px, 10px, 53px, 2px);
+msgMarginTopAttached: 3px;
msgLnkPadding: 2px; // for media open / save links
msgBorder: #f0f0f0;
msgInBg: #fff;
@@ -1085,11 +1088,27 @@ msgInReplyBarColor: #2fa9e2;
msgOutReplyBarSelColor: #4da79f;
msgInReplyBarSelColor: #2fa9e2;
+msgBotKbDuration: 200;
+msgBotKbFont: semiboldFont;
+msgBotKbOverOpacity: 0.1;
+msgBotKbIconPadding: 2px;
+msgBotKbUrlIcon: sprite(188px, 338px, 10px, 10px);
+//msgBotKbRequestPhoneIcon: msgBotKbUrlIcon;
+//msgBotKbRequestLocationIcon: msgBotKbUrlIcon;
+msgBotKbSwitchPmIcon: sprite(188px, 348px, 10px, 10px);
+msgBotKbButton: botKeyboardButton {
+ margin: 5px;
+ padding: 10px;
+ height: 36px;
+ textTop: 8px;
+ downTextTop: 9px;
+}
+
msgServiceBg: #89a0b47f;
msgServiceSelectBg: #bbc8d4a2;
msgServiceColor: #FFF;
msgServicePadding: margins(12px, 3px, 12px, 4px);
-msgServiceMargin: margins(10px, 9px, 80px, 5px);
+msgServiceMargin: margins(10px, 10px, 80px, 2px);
msgColor: #000;
msgDateColor: #000;
@@ -1473,6 +1492,11 @@ replyCancel: iconedButton(btnDefIconed) {
width: 49px;
height: 49px;
}
+inlineBotCancel: iconedButton(replyCancel) {
+ height: 46px;
+ iconPos: point(-1px, 16px); // < 0 means draw in the center of the button
+ downIconPos: point(-1px, 17px);
+}
forwardIcon: sprite(368px, 197px, 24px, 24px);
historyScroll: flatScroll(scrollDef) {
@@ -1527,6 +1551,7 @@ reportSpamBg: #fffffff0;
newMsgSound: ':/gui/art/newmsg.wav';
unreadBarHeight: 32px;
+unreadBarMargin: 8px;
unreadBarFont: semiboldFont;
unreadBarBG: #fcfbfa;
unreadBarBorder: shadowColor;
@@ -2097,16 +2122,16 @@ verifiedCheckInv: sprite(299px, 221px, 14px, 14px);
verifiedCheckPos: point(4px, 2px);
botKbDuration: 200;
-botKbBg: #f7f7f7;
-botKbOverBg: #e8ecef;
-botKbDownBg: #dfe3e6;
-botKbColor: #8a8a8f;
-botKbFont: font(16px);
+botKbBg: #edf1f5;
+botKbOverBg: #d8e2ec;
+botKbDownBg: #d8e2ec;
+botKbColor: #4b565f;
+botKbFont: font(15px semibold);
botKbButton: botKeyboardButton {
margin: 10px;
padding: 10px;
- height: 36px;
- textTop: 8px;
+ height: 38px;
+ textTop: 9px;
downTextTop: 9px;
}
botKbTinyButton: botKeyboardButton {
@@ -2114,12 +2139,17 @@ botKbTinyButton: botKeyboardButton {
padding: 3px;
height: 25px;
textTop: 2px;
- downTextTop: 3px;
+ downTextTop: 2px;
}
botKbScroll: flatScroll(solidScroll) {
deltax: 3px;
width: 10px;
}
+switchPmButton: BoxButton(defaultBoxButton) {
+ width: 320px;
+ height: 34px;
+ textTop: 7px;
+}
minPhotoSize: 100px;
maxMediaSize: 420px;
@@ -2460,7 +2490,7 @@ linksDateMargin: margins(0px, 15px, 0px, 2px);
linksPhotoCheck: sprite(184px, 196px, 16px, 16px);
linksPhotoChecked: sprite(168px, 196px, 16px, 16px);
-inlineResultsLeft: 15px;
+inlineResultsLeft: 11px;
inlineResultsSkip: 3px;
inlineMediaHeight: 96px;
inlineThumbSize: 64px;
@@ -2469,6 +2499,8 @@ inlineDescriptionFg: #8a8a8a;
inlineRowMargin: 6px;
inlineRowBorder: linksBorder;
inlineRowBorderFg: linksBorderFg;
+inlineRowFileNameTop: 2px;
+inlineRowFileDescriptionTop: 23px;
inlineResultsMinWidth: 64px;
inlineDurationMargin: 3px;
@@ -2476,3 +2508,17 @@ editTextArea: InputArea(defaultInputArea) {
textMargins: margins(1px, 6px, 1px, 4px);
heightMax: 256px;
}
+
+toastFont: normalFont;
+toastMaxWidth: 480px;
+toastMinMargin: 13px;
+toastBg: medviewSaveMsg;
+toastFg: #FFF;
+toastPadding: margins(19px, 13px, 19px, 12px);
+toastFadeInDuration: 200;
+toastFadeOutDuration: 1000;
+
+infoButton: PeerAvatarButton {
+ size: topBarHeight;
+ photoSize: 42px;
+}
diff --git a/Telegram/Resources/style_classes.txt b/Telegram/Resources/style_classes.txt
index 229b3398b..a3405a048 100644
--- a/Telegram/Resources/style_classes.txt
+++ b/Telegram/Resources/style_classes.txt
@@ -405,3 +405,8 @@ InputField {
iconSprite: sprite;
iconPosition: point;
}
+
+PeerAvatarButton {
+ size: number;
+ photoSize: number;
+}
diff --git a/Telegram/SourceFiles/telegram.qrc b/Telegram/Resources/telegram.qrc
similarity index 100%
rename from Telegram/SourceFiles/telegram.qrc
rename to Telegram/Resources/telegram.qrc
diff --git a/Telegram/SourceFiles/telegram_emojis.qrc b/Telegram/Resources/telegram_emojis.qrc
similarity index 100%
rename from Telegram/SourceFiles/telegram_emojis.qrc
rename to Telegram/Resources/telegram_emojis.qrc
diff --git a/Telegram/SourceFiles/telegram_linux.qrc b/Telegram/Resources/telegram_linux.qrc
similarity index 100%
rename from Telegram/SourceFiles/telegram_linux.qrc
rename to Telegram/Resources/telegram_linux.qrc
diff --git a/Telegram/SourceFiles/telegram_mac.qrc b/Telegram/Resources/telegram_mac.qrc
similarity index 100%
rename from Telegram/SourceFiles/telegram_mac.qrc
rename to Telegram/Resources/telegram_mac.qrc
diff --git a/Telegram/SourceFiles/telegram_wnd.qrc b/Telegram/Resources/telegram_wnd.qrc
similarity index 100%
rename from Telegram/SourceFiles/telegram_wnd.qrc
rename to Telegram/Resources/telegram_wnd.qrc
diff --git a/Telegram/Setup.iss b/Telegram/Setup.iss
index c3773c30c..9793ed439 100644
--- a/Telegram/Setup.iss
+++ b/Telegram/Setup.iss
@@ -25,10 +25,10 @@ DefaultGroupName={#MyAppName}
AllowNoIcons=yes
OutputDir=.\..\Win32\Deploy
OutputBaseFilename=tsetup.{#MyAppVersionFull}
-SetupIconFile=.\SourceFiles\art\icon256.ico
+SetupIconFile=.\Resources\art\icon256.ico
UninstallDisplayIcon={app}\Telegram.exe
Compression=lzma
-SolidCompression=yes
+SolidCompression=yes
DisableStartupPrompt=yes
PrivilegesRequired=lowest
VersionInfoVersion={#MyAppVersion}.0
diff --git a/Telegram/SourceFiles/Telegram.plist b/Telegram/SourceFiles/Telegram.plist
deleted file mode 100644
index d8286c37a..000000000
--- a/Telegram/SourceFiles/Telegram.plist
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- NSPrincipalClass
- NSApplication
- CFBundleIconFile
-
- CFBundlePackageType
- APPL
- CFBundleGetInfoString
- Created by Qt/QMake
- CFBundleSignature
- ????
- CFBundleExecutable
- Telegram
- CFBundleIdentifier
- com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
- NOTE
- This file was generated by Qt/QMake.
-
-
diff --git a/Telegram/SourceFiles/_other/genemoji.cpp b/Telegram/SourceFiles/_other/genemoji.cpp
index 22e3fd1bd..544da56bc 100644
--- a/Telegram/SourceFiles/_other/genemoji.cpp
+++ b/Telegram/SourceFiles/_other/genemoji.cpp
@@ -1944,7 +1944,7 @@ to link the code of portions of this program with the OpenSSL library.\n\
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
*/\n";
- tcpp << "#include \"stdafx.h\"\n#include \"gui/emoji_config.h\"\n\n";
+ tcpp << "#include \"stdafx.h\"\n#include \"ui/emoji_config.h\"\n\n";
tcpp << "namespace {\n"; // namespace with data
tcpp << "\tEmojiData *emojis = 0;\n";
diff --git a/Telegram/SourceFiles/_other/genlang.cpp b/Telegram/SourceFiles/_other/genlang.cpp
index 614f81de5..3026fb6bf 100644
--- a/Telegram/SourceFiles/_other/genlang.cpp
+++ b/Telegram/SourceFiles/_other/genlang.cpp
@@ -432,7 +432,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
QMap > &countedTags(keysCounted[keysOrder[i]]);
if (!countedTags.isEmpty()) {
for (QMap >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
- const QVector &counted(*j);
+ const auto &counted(*j);
for (int k = 0, s = counted.size(); k < s; ++k) {
th << "\t" << keysOrder[i] << "__" + j.key() + QString::number(k).toUtf8() << ",\n";
}
@@ -510,7 +510,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
QMap > &countedTags(keysCounted[keysOrder[i]]);
if (!countedTags.isEmpty()) {
for (QMap >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
- const QVector &counted(*j);
+ const auto &counted(*j);
for (int k = 0, s = counted.size(); k < s; ++k) {
tcpp << "\t\t\"" << keysOrder[i] << "__" + j.key() + QString::number(k).toUtf8() << "\",\n";
}
@@ -534,7 +534,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
QMap > &countedTags(keysCounted[keysOrder[i]]);
if (!countedTags.isEmpty()) {
for (QMap >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
- const QVector &counted(*j);
+ const auto &counted(*j);
for (int k = 0, s = counted.size(); k < s; ++k) {
writeCppKey(tcpp, keysOrder[i] + "__" + j.key() + QString::number(k).toUtf8(), counted[k]);
}
diff --git a/Telegram/SourceFiles/_other/genstyles.cpp b/Telegram/SourceFiles/_other/genstyles.cpp
index 67f58191f..3d52dc82c 100644
--- a/Telegram/SourceFiles/_other/genstyles.cpp
+++ b/Telegram/SourceFiles/_other/genstyles.cpp
@@ -383,7 +383,7 @@ to link the code of portions of this program with the OpenSSL library.\n\
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
*/\n";
- tout << "#pragma once\n\n#include \"style.h\"\n\nnamespace style {\n";
+ tout << "#pragma once\n\n#include \"ui/style.h\"\n\nnamespace style {\n";
for (int i = 0, l = byIndex.size(); i < l; ++i) {
ClassData &cls(byIndex[i]);
classes.insert(cls.name, cls);
@@ -1064,7 +1064,7 @@ ScalarValue prepareFont(int variant, const string &name, const char *&text, cons
string size, family;
int flags = 0;
- bool sizepx;
+ bool sizepx = false;
readStyleGenToken(text, end, type, token);
if (type != stConsStart) throw Exception(QString("Unexpected token %1 (%2) while reading font() cons!").arg(type).arg(token.c_str()));
@@ -1544,7 +1544,7 @@ to link the code of portions of this program with the OpenSSL library.\n\
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
*/\n";
- tout << "#pragma once\n\n#include \"style.h\"\n\nnamespace st {\n";
+ tout << "#pragma once\n\n#include \"ui/style.h\"\n\nnamespace st {\n";
tcpp << "\
/*\n\
Created from \'/Resources/style.txt\' by \'/MetaStyle\' project\n\
diff --git a/Telegram/SourceFiles/_other/memain.cpp b/Telegram/SourceFiles/_other/memain.cpp
index aaf5b6341..81b1692ca 100644
--- a/Telegram/SourceFiles/_other/memain.cpp
+++ b/Telegram/SourceFiles/_other/memain.cpp
@@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "memain.h"
int main(int argc, char *argv[]) {
- QString emoji_in("./SourceFiles/art/emojisprite_"), emoji_out("./SourceFiles/gui/emoji_config.cpp"), emoji_png("./SourceFiles/art/emoji");
+ QString emoji_in("./Resources/art/emojisprite_"), emoji_out("./SourceFiles/gui/emoji_config.cpp"), emoji_png("./Resources/art/emoji");
for (int i = 0; i < argc; ++i) {
if (string("-emoji_in") == argv[i]) {
if (++i < argc) emoji_in = argv[i];
diff --git a/Telegram/SourceFiles/_other/msmain.cpp b/Telegram/SourceFiles/_other/msmain.cpp
index ac85f52e4..c59c7dbc4 100644
--- a/Telegram/SourceFiles/_other/msmain.cpp
+++ b/Telegram/SourceFiles/_other/msmain.cpp
@@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include
int main(int argc, char *argv[]) {
- QString classes_in("style_classes.txt"), classes_out("style_classes.h"), styles_in("style.txt"), styles_out("style_auto.h"), path_to_sprites("./SourceFiles/art/");
+ QString classes_in("style_classes.txt"), classes_out("style_classes.h"), styles_in("style.txt"), styles_out("style_auto.h"), path_to_sprites("./Resources/art/");
for (int i = 0; i < argc; ++i) {
if (string("-classes_in") == argv[i]) {
if (++i < argc) classes_in = argv[i];
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 720e37ccc..62adaec33 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -19,11 +19,11 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
-#include "style.h"
+#include "ui/style.h"
#include "lang.h"
#include "application.h"
-#include "window.h"
+#include "mainwindow.h"
#include "mainwidget.h"
#include "apiwrap.h"
@@ -99,21 +99,21 @@ void ApiWrap::resolveMessageDatas() {
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
switch (msgs.type()) {
case mtpc_messages_messages: {
- const MTPDmessages_messages &d(msgs.c_messages_messages());
+ const auto &d(msgs.c_messages_messages());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
App::feedMsgs(d.vmessages, NewMessageExisting);
} break;
case mtpc_messages_messagesSlice: {
- const MTPDmessages_messagesSlice &d(msgs.c_messages_messagesSlice());
+ const auto &d(msgs.c_messages_messagesSlice());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
App::feedMsgs(d.vmessages, NewMessageExisting);
} break;
case mtpc_messages_channelMessages: {
- const MTPDmessages_channelMessages &d(msgs.c_messages_channelMessages());
+ const auto &d(msgs.c_messages_channelMessages());
if (channel) {
channel->ptsReceived(d.vpts.v);
} else {
@@ -169,8 +169,8 @@ void ApiWrap::processFullPeer(PeerData *peer, const MTPUserFull &result) {
}
void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req) {
- const MTPDmessages_chatFull &d(result.c_messages_chatFull());
- const QVector &vc(d.vchats.c_vector().v);
+ const auto &d(result.c_messages_chatFull());
+ const auto &vc(d.vchats.c_vector().v);
bool badVersion = false;
if (peer->isChat()) {
badVersion = (!vc.isEmpty() && vc.at(0).type() == mtpc_chat && vc.at(0).c_chat().vversion.v < peer->asChat()->version);
@@ -186,13 +186,13 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
LOG(("MTP Error: bad type in gotChatFull for chat: %1").arg(d.vfull_chat.type()));
return;
}
- const MTPDchatFull &f(d.vfull_chat.c_chatFull());
+ const auto &f(d.vfull_chat.c_chatFull());
App::feedParticipants(f.vparticipants, false, false);
- const QVector &v(f.vbot_info.c_vector().v);
+ const auto &v(f.vbot_info.c_vector().v);
for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i < e; ++i) {
switch (i->type()) {
case mtpc_botInfo: {
- const MTPDbotInfo &b(i->c_botInfo());
+ const auto &b(i->c_botInfo());
UserData *user = App::userLoaded(b.vuser_id.v);
if (user) {
user->setBotInfo(*i);
@@ -218,7 +218,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
LOG(("MTP Error: bad type in gotChatFull for channel: %1").arg(d.vfull_chat.type()));
return;
}
- const MTPDchannelFull &f(d.vfull_chat.c_channelFull());
+ const auto &f(d.vfull_chat.c_channelFull());
PhotoData *photo = App::feedPhoto(f.vchat_photo);
ChannelData *channel = peer->asChannel();
channel->flagsFull = f.vflags.v;
@@ -245,7 +245,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
if (!h->isEmpty()) {
h->clear(true);
}
- if (hto->inChatList() && h->inChatList()) {
+ if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) {
App::removeDialog(h);
}
}
@@ -257,11 +257,11 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
App::main()->peerUpdated(cfrom);
}
}
- const QVector &v(f.vbot_info.c_vector().v);
+ const auto &v(f.vbot_info.c_vector().v);
for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i < e; ++i) {
switch (i->type()) {
case mtpc_botInfo: {
- const MTPDbotInfo &b(i->c_botInfo());
+ const auto &b(i->c_botInfo());
UserData *user = App::userLoaded(b.vuser_id.v);
if (user) {
user->setBotInfo(*i);
@@ -321,7 +321,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
}
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req) {
- const MTPDuserFull &d(result.c_userFull());
+ const auto &d(result.c_userFull());
App::feedUsers(MTP_vector(1, d.vuser), false);
if (d.has_profile_photo()) {
App::feedPhoto(d.vprofile_photo);
@@ -351,7 +351,7 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI
}
bool ApiWrap::gotPeerFullFailed(PeerData *peer, const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_fullPeerRequests.remove(peer);
return true;
@@ -399,7 +399,7 @@ void ApiWrap::requestLastParticipants(ChannelData *peer, bool fromStart) {
if ((needAdmins && adminsOutdated) || peer->lastParticipantsCountOutdated()) {
fromStart = true;
}
- QMap::iterator i = _participantsRequests.find(peer);
+ auto i = _participantsRequests.find(peer);
if (i != _participantsRequests.cend()) {
if (fromStart && i.value() < 0) { // was not loading from start
_participantsRequests.erase(i);
@@ -420,7 +420,7 @@ void ApiWrap::gotChat(PeerData *peer, const MTPmessages_Chats &result) {
_peerRequests.remove(peer);
if (result.type() == mtpc_messages_chats) {
- const QVector &v(result.c_messages_chats().vchats.c_vector().v);
+ const auto &v(result.c_messages_chats().vchats.c_vector().v);
bool badVersion = false;
if (peer->isChat()) {
badVersion = (!v.isEmpty() && v.at(0).type() == mtpc_chat && v.at(0).c_chat().vversion.v < peer->asChat()->version);
@@ -458,7 +458,7 @@ void ApiWrap::gotUsers(const MTPVector &result) {
}
bool ApiWrap::gotPeerFailed(PeerData *peer, const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_peerRequests.remove(peer);
return true;
@@ -491,8 +491,8 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP
peer->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate;
}
- const MTPDchannels_channelParticipants &d(result.c_channels_channelParticipants());
- const QVector &v(d.vparticipants.c_vector().v);
+ const auto &d(result.c_channels_channelParticipants());
+ const auto &v(d.vparticipants.c_vector().v);
App::feedUsers(d.vusers);
bool added = false, needBotsInfos = false;
int32 botStatus = peer->mgInfo->botStatus;
@@ -555,7 +555,7 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP
}
bool ApiWrap::lastParticipantsFail(ChannelData *peer, const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (_participantsRequests.value(peer) == req || _participantsRequests.value(peer) == -req) {
_participantsRequests.remove(peer);
} else if (_botsRequests.value(peer) == req) {
@@ -578,27 +578,27 @@ void ApiWrap::gotSelfParticipant(ChannelData *channel, const MTPchannels_Channel
return;
}
- const MTPDchannels_channelParticipant &p(result.c_channels_channelParticipant());
+ const auto &p(result.c_channels_channelParticipant());
App::feedUsers(p.vusers);
switch (p.vparticipant.type()) {
case mtpc_channelParticipantSelf: {
- const MTPDchannelParticipantSelf &d(p.vparticipant.c_channelParticipantSelf());
+ const auto &d(p.vparticipant.c_channelParticipantSelf());
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
case mtpc_channelParticipantCreator: {
- const MTPDchannelParticipantCreator &d(p.vparticipant.c_channelParticipantCreator());
+ const auto &d(p.vparticipant.c_channelParticipantCreator());
channel->inviter = MTP::authedId();
channel->inviteDate = date(MTP_int(channel->date));
} break;
case mtpc_channelParticipantModerator: {
- const MTPDchannelParticipantModerator &d(p.vparticipant.c_channelParticipantModerator());
+ const auto &d(p.vparticipant.c_channelParticipantModerator());
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
case mtpc_channelParticipantEditor: {
- const MTPDchannelParticipantEditor &d(p.vparticipant.c_channelParticipantEditor());
+ const auto &d(p.vparticipant.c_channelParticipantEditor());
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
@@ -609,7 +609,7 @@ void ApiWrap::gotSelfParticipant(ChannelData *channel, const MTPchannels_Channel
}
bool ApiWrap::gotSelfParticipantFail(ChannelData *channel, const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (error.type() == qstr("USER_NOT_PARTICIPANT")) {
channel->inviter = -1;
@@ -655,7 +655,7 @@ void ApiWrap::kickParticipantDone(KickRequest kick, const MTPUpdates &result, mt
}
bool ApiWrap::kickParticipantFail(KickRequest kick, const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_kickRequests.remove(kick);
return true;
}
@@ -680,10 +680,10 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
_stickerSetRequests.remove(setId);
if (result.type() != mtpc_messages_stickerSet) return;
- const MTPDmessages_stickerSet &d(result.c_messages_stickerSet());
+ const auto &d(result.c_messages_stickerSet());
if (d.vset.type() != mtpc_stickerSet) return;
- const MTPDstickerSet &s(d.vset.c_stickerSet());
+ const auto &s(d.vset.c_stickerSet());
Stickers::Sets &sets(Global::RefStickerSets());
auto it = sets.find(setId);
@@ -695,7 +695,7 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
it->title = stickerSetTitle(s);
it->flags = s.vflags.v;
- const QVector &d_docs(d.vdocuments.c_vector().v);
+ const auto &d_docs(d.vdocuments.c_vector().v);
auto custom = sets.find(Stickers::CustomSetId);
StickerPack pack;
@@ -735,13 +735,13 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
} else {
it->stickers = pack;
it->emoji.clear();
- const QVector &v(d.vpacks.c_vector().v);
+ const auto &v(d.vpacks.c_vector().v);
for (int32 i = 0, l = v.size(); i < l; ++i) {
if (v.at(i).type() != mtpc_stickerPack) continue;
- const MTPDstickerPack &pack(v.at(i).c_stickerPack());
+ const auto &pack(v.at(i).c_stickerPack());
if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
- const QVector &stickers(pack.vdocuments.c_vector().v);
+ const auto &stickers(pack.vdocuments.c_vector().v);
StickerPack p;
p.reserve(stickers.size());
for (int32 j = 0, c = stickers.size(); j < c; ++j) {
@@ -765,7 +765,7 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
}
bool ApiWrap::gotStickerSetFail(uint64 setId, const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_stickerSetRequests.remove(setId);
return true;
@@ -861,21 +861,21 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
const QVector *v = 0;
switch (msgs.type()) {
case mtpc_messages_messages: {
- const MTPDmessages_messages &d(msgs.c_messages_messages());
+ const auto &d(msgs.c_messages_messages());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.c_vector().v;
} break;
case mtpc_messages_messagesSlice: {
- const MTPDmessages_messagesSlice &d(msgs.c_messages_messagesSlice());
+ const auto &d(msgs.c_messages_messagesSlice());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.c_vector().v;
} break;
case mtpc_messages_channelMessages: {
- const MTPDmessages_channelMessages &d(msgs.c_messages_channelMessages());
+ const auto &d(msgs.c_messages_channelMessages());
if (channel) {
channel->ptsReceived(d.vpts.v);
} else {
@@ -894,7 +894,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
if (!v) return;
QMap msgsIds; // copied from feedMsgs
for (int32 i = 0, l = v->size(); i < l; ++i) {
- const MTPMessage &msg(v->at(i));
+ const auto &msg(v->at(i));
switch (msg.type()) {
case mtpc_message: msgsIds.insert((uint64(uint32(msg.c_message().vid.v)) << 32) | uint64(i), i); break;
case mtpc_messageEmpty: msgsIds.insert((uint64(uint32(msg.c_messageEmpty().vid.v)) << 32) | uint64(i), i); break;
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 3ea5eca0f..8845d90bd 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -28,7 +28,7 @@ public:
ApiWrap(QObject *parent);
void init();
- typedef SharedCallback2 RequestMessageDataCallback;
+ typedef SharedCallback RequestMessageDataCallback;
void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback);
void requestFullPeer(PeerData *peer);
@@ -69,7 +69,7 @@ private:
struct MessageDataRequest {
MessageDataRequest() : req(0) {
}
- typedef SharedCallback2::Ptr CallbackPtr;
+ typedef SharedCallback::Ptr CallbackPtr;
typedef QList Callbacks;
mtpRequestId req;
Callbacks callbacks;
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 4887c2681..c227c243b 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -19,8 +19,10 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
-#include "lang.h"
+#include "app.h"
+#include "lang.h"
+#include "dialogs/dialogs_layout.h"
#include "audio.h"
#include "application.h"
#include "fileuploader.h"
@@ -29,7 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include
#endif
#include "localstorage.h"
-
+#include "apiwrap.h"
#include "numbers.h"
namespace {
@@ -55,12 +57,6 @@ namespace {
typedef QHash WebPagesData;
WebPagesData webPagesData;
- typedef QMap ReplyMarkups;
- ReplyMarkups replyMarkups;
- ReplyMarkup zeroMarkup(qFlags(MTPDreplyKeyboardMarkup_ClientFlag::f_zero));
- typedef QMap ChannelReplyMarkups;
- ChannelReplyMarkups channelReplyMarkups;
-
PhotoItems photoItems;
DocumentItems documentItems;
WebPageItems webPageItems;
@@ -111,9 +107,6 @@ namespace {
typedef QHash LastPhotosMap;
LastPhotosMap lastPhotosMap;
- typedef QMap InlineResultLoaders;
- InlineResultLoaders inlineResultLoaders;
-
style::color _msgServiceBg;
style::color _msgServiceSelectBg;
style::color _historyScrollBarColor;
@@ -153,23 +146,29 @@ namespace App {
return AppClass::app();
}
- Window *wnd() {
+ MainWindow *wnd() {
return AppClass::wnd();
}
MainWidget *main() {
- Window *w(wnd());
- return w ? w->mainWidget() : 0;
+ if (auto w = wnd()) {
+ return w->mainWidget();
+ }
+ return nullptr;
}
SettingsWidget *settings() {
- Window *w(wnd());
- return w ? w->settingsWidget() : 0;
+ if (auto w = wnd()) {
+ return w->settingsWidget();
+ }
+ return nullptr;
}
bool passcoded() {
- Window *w(wnd());
- return w ? w->passcodeWidget() : 0;
+ if (auto w = wnd()) {
+ return w->passcodeWidget();
+ }
+ return false;
}
FileUploader *uploader() {
@@ -180,21 +179,19 @@ namespace App {
return main() ? main()->api() : 0;
}
+namespace {
bool loggedOut() {
- Window *w(wnd());
if (cHasPasscode()) {
cSetHasPasscode(false);
}
if (audioPlayer()) {
audioPlayer()->stopAndClear();
}
- if (w) {
+ if (auto w = wnd()) {
w->tempDirDelete(Local::ClearManagerAll);
w->notifyClearFast();
w->setupIntro(true);
}
- MainWidget *m(main());
- if (m) m->destroyData();
MTP::authed(0);
Local::reset();
@@ -205,13 +202,14 @@ namespace App {
globalNotifyChatsPtr = UnknownNotifySettings;
if (App::uploader()) App::uploader()->clear();
clearStorageImages();
- if (w) {
+ if (auto w = wnd()) {
w->getTitle()->updateBackButton();
w->updateTitleStatus();
w->getTitle()->resizeEvent(0);
}
return true;
}
+} // namespace
void logOut() {
if (MTP::started()) {
@@ -366,16 +364,16 @@ namespace App {
UserData *feedUsers(const MTPVector &users, bool emitPeerUpdated) {
UserData *data = 0;
- const QVector &v(users.c_vector().v);
+ const auto &v(users.c_vector().v);
for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
- const MTPuser &user(*i);
+ const auto &user(*i);
data = 0;
bool wasContact = false, minimal = false;
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
switch (user.type()) {
case mtpc_userEmpty: {
- const MTPDuserEmpty &d(user.c_userEmpty());
+ const auto &d(user.c_userEmpty());
PeerId peer(peerFromUser(d.vid.v));
data = App::user(peer);
@@ -391,7 +389,7 @@ namespace App {
data->contact = -1;
} break;
case mtpc_user: {
- const MTPDuser &d(user.c_user());
+ const auto &d(user.c_user());
minimal = d.is_min();
PeerId peer(peerFromUser(d.vid.v));
@@ -437,13 +435,17 @@ namespace App {
bool showPhone = !isServiceUser(data->id) && !d.is_self() && !d.is_contact() && !d.is_mutual_contact();
bool showPhoneChanged = !isServiceUser(data->id) && !d.is_self() && ((showPhone && data->contact) || (!showPhone && !data->contact));
+ if (minimal) {
+ showPhoneChanged = false;
+ showPhone = !isServiceUser(data->id) && (data->id != peerFromUser(MTP::authedId())) && !data->contact;
+ }
// see also Local::readPeer
QString pname = (showPhoneChanged || phoneChanged || nameChanged) ? ((showPhone && !phone.isEmpty()) ? formatPhone(phone) : QString()) : data->nameOrPhone;
if (!minimal && d.is_self() && uname != data->username) {
- SignalHandlers::setSelfUsername(uname);
+ SignalHandlers::setCrashAnnotation("Username", uname);
}
data->setName(fname, lname, pname, uname);
if (d.has_photo()) {
@@ -520,14 +522,14 @@ namespace App {
PeerData *feedChats(const MTPVector &chats, bool emitPeerUpdated) {
PeerData *data = 0;
- const QVector &v(chats.c_vector().v);
+ const auto &v(chats.c_vector().v);
for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
- const MTPchat &chat(*i);
+ const auto &chat(*i);
data = 0;
bool minimal = false;
switch (chat.type()) {
case mtpc_chat: {
- const MTPDchat &d(chat.c_chat());
+ const auto &d(chat.c_chat());
data = App::chat(peerFromChat(d.vid.v));
data->input = MTP_inputPeerChat(d.vid);
@@ -539,7 +541,7 @@ namespace App {
cdata->date = d.vdate.v;
if (d.has_migrated_to() && d.vmigrated_to.type() == mtpc_inputChannel) {
- const MTPDinputChannel &c(d.vmigrated_to.c_inputChannel());
+ const auto &c(d.vmigrated_to.c_inputChannel());
ChannelData *channel = App::channel(peerFromChannel(c.vchannel_id));
if (!channel->mgInfo) {
channel->flags |= MTPDchannel::Flag::f_megagroup;
@@ -561,7 +563,7 @@ namespace App {
if (!h->isEmpty()) {
h->clear(true);
}
- if (hto->inChatList() && h->inChatList()) {
+ if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) {
App::removeDialog(h);
}
}
@@ -586,7 +588,7 @@ namespace App {
}
} break;
case mtpc_chatForbidden: {
- const MTPDchatForbidden &d(chat.c_chatForbidden());
+ const auto &d(chat.c_chatForbidden());
data = App::chat(peerFromChat(d.vid.v));
data->input = MTP_inputPeerChat(d.vid);
@@ -602,7 +604,7 @@ namespace App {
cdata->isForbidden = true;
} break;
case mtpc_channel: {
- const MTPDchannel &d(chat.c_channel());
+ const auto &d(chat.c_channel());
PeerId peer(peerFromChannel(d.vid.v));
minimal = d.is_min();
@@ -642,7 +644,7 @@ namespace App {
cdata->setPhoto(d.vphoto);
} break;
case mtpc_channelForbidden: {
- const MTPDchannelForbidden &d(chat.c_channelForbidden());
+ const auto &d(chat.c_channelForbidden());
PeerId peer(peerFromChannel(d.vid.v));
data = App::channel(peer);
@@ -684,18 +686,18 @@ namespace App {
ChatData *chat = 0;
switch (p.type()) {
case mtpc_chatParticipantsForbidden: {
- const MTPDchatParticipantsForbidden &d(p.c_chatParticipantsForbidden());
+ const auto &d(p.c_chatParticipantsForbidden());
chat = App::chat(d.vchat_id.v);
chat->count = -1;
chat->invalidateParticipants();
} break;
case mtpc_chatParticipants: {
- const MTPDchatParticipants &d(p.c_chatParticipants());
+ const auto &d(p.c_chatParticipants());
chat = App::chat(d.vchat_id.v);
if (!requestBotInfos || chat->version <= d.vversion.v) { // !requestBotInfos is true on getFullChat result
chat->version = d.vversion.v;
- const QVector &v(d.vparticipants.c_vector().v);
+ const auto &v(d.vparticipants.c_vector().v);
chat->count = v.size();
int32 pversion = chat->participants.isEmpty() ? 1 : (chat->participants.begin().value() + 1);
chat->invitedByMe = ChatData::InvitedByMe();
@@ -705,17 +707,17 @@ namespace App {
int32 uid = 0, inviter = 0;
switch (i->type()) {
case mtpc_chatParticipantCreator: {
- const MTPDchatParticipantCreator &p(i->c_chatParticipantCreator());
+ const auto &p(i->c_chatParticipantCreator());
uid = p.vuser_id.v;
chat->creator = uid;
} break;
case mtpc_chatParticipantAdmin: {
- const MTPDchatParticipantAdmin &p(i->c_chatParticipantAdmin());
+ const auto &p(i->c_chatParticipantAdmin());
uid = p.vuser_id.v;
inviter = p.vinviter_id.v;
} break;
case mtpc_chatParticipant: {
- const MTPDchatParticipant &p(i->c_chatParticipant());
+ const auto &p(i->c_chatParticipant());
uid = p.vuser_id.v;
inviter = p.vinviter_id.v;
} break;
@@ -965,7 +967,7 @@ namespace App {
}
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());
- existing->updateMedia(m.has_media() ? (&m.vmedia) : 0);
+ existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr);
existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
existing->addToOverview(AddToOverviewNew);
@@ -985,15 +987,7 @@ namespace App {
peerId = peerFromUser(m.vfrom_id);
}
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());
- existing->updateMedia(m.has_media() ? (&m.vmedia) : 0, true);
- existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
- if (existing->history()->textCachedFor == existing) {
- existing->history()->textCachedFor = 0;
- }
- if (App::main()) {
- App::main()->dlgUpdated(existing->history(), existing->id);
- }
+ existing->applyEdition(m);
}
}
@@ -1027,10 +1021,10 @@ namespace App {
void feedMsgs(const QVector &msgs, NewMessageType type) {
QMap msgsIds;
for (int32 i = 0, l = msgs.size(); i < l; ++i) {
- const MTPMessage &msg(msgs.at(i));
+ const auto &msg(msgs.at(i));
switch (msg.type()) {
case mtpc_message: {
- const MTPDmessage &d(msg.c_message());
+ const auto &d(msg.c_message());
bool needToAdd = true;
if (type == NewMessageUnread) { // new message, index my forwarded messages to links overview
if (checkEntitiesAndViewsUpdate(d)) { // already in blocks
@@ -1058,17 +1052,17 @@ namespace App {
ImagePtr image(const MTPPhotoSize &size) {
switch (size.type()) {
case mtpc_photoSize: {
- const MTPDphotoSize &d(size.c_photoSize());
+ const auto &d(size.c_photoSize());
if (d.vlocation.type() == mtpc_fileLocation) {
- const MTPDfileLocation &l(d.vlocation.c_fileLocation());
+ const auto &l(d.vlocation.c_fileLocation());
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), d.vsize.v);
}
} break;
case mtpc_photoCachedSize: {
- const MTPDphotoCachedSize &d(size.c_photoCachedSize());
+ const auto &d(size.c_photoCachedSize());
if (d.vlocation.type() == mtpc_fileLocation) {
- const MTPDfileLocation &l(d.vlocation.c_fileLocation());
- const string &s(d.vbytes.c_string().v);
+ const auto &l(d.vlocation.c_fileLocation());
+ const auto &s(d.vbytes.c_string().v);
QByteArray bytes(s.data(), s.size());
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), bytes);
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
@@ -1083,7 +1077,7 @@ namespace App {
StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc) {
if (loc.type() == mtpc_fileLocation) {
- const MTPDfileLocation &l(loc.c_fileLocation());
+ const auto &l(loc.c_fileLocation());
return StorageImageLocation(w, h, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
}
return StorageImageLocation(w, h, 0, 0, 0, 0);
@@ -1092,11 +1086,11 @@ namespace App {
StorageImageLocation imageLocation(const MTPPhotoSize &size) {
switch (size.type()) {
case mtpc_photoSize: {
- const MTPDphotoSize &d(size.c_photoSize());
+ const auto &d(size.c_photoSize());
return imageLocation(d.vw.v, d.vh.v, d.vlocation);
} break;
case mtpc_photoCachedSize: {
- const MTPDphotoCachedSize &d(size.c_photoCachedSize());
+ const auto &d(size.c_photoCachedSize());
return imageLocation(d.vw.v, d.vh.v, d.vlocation);
} break;
}
@@ -1149,8 +1143,8 @@ namespace App {
} else {
if (channelHistory) {
channelHistory->messageWithIdDeleted(i->v);
- if (channelHistory->unreadCount > 0 && i->v >= channelHistory->inboxReadBefore) {
- channelHistory->setUnreadCount(channelHistory->unreadCount - 1);
+ if (channelHistory->unreadCount() > 0 && i->v >= channelHistory->inboxReadBefore) {
+ channelHistory->setUnreadCount(channelHistory->unreadCount() - 1);
}
}
}
@@ -1163,9 +1157,9 @@ namespace App {
}
void feedUserLinks(const MTPVector &links, bool emitPeerUpdated) {
- const QVector &v(links.c_vector().v);
+ const auto &v(links.c_vector().v);
for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
- const MTPDcontacts_link &dv(i->c_contacts_link());
+ const auto &dv(i->c_contacts_link());
UserData *user = feedUsers(MTP_vector(1, dv.vuser), false);
MTPint userId(MTP_int(0));
switch (dv.vuser.type()) {
@@ -1298,7 +1292,7 @@ namespace App {
}
switch (photo.type()) {
case mtpc_photo: {
- const MTPDphoto &ph(photo.c_photo());
+ const auto &ph(photo.c_photo());
return App::photoSet(ph.vid.v, 0, ph.vaccess_hash.v, ph.vdate.v, ImagePtr(*thumb, "JPG"), ImagePtr(*medium, "JPG"), ImagePtr(*full, "JPG"));
} break;
case mtpc_photoEmpty: return App::photo(photo.c_photoEmpty().vid.v);
@@ -1307,7 +1301,7 @@ namespace App {
}
PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert) {
- const QVector &sizes(photo.vsizes.c_vector().v);
+ const auto &sizes(photo.vsizes.c_vector().v);
const MTPPhotoSize *thumb = 0, *medium = 0, *full = 0;
int32 thumbLevel = -1, mediumLevel = -1, fullLevel = -1;
for (QVector::const_iterator i = sizes.cbegin(), e = sizes.cend(); i != e; ++i) {
@@ -1362,7 +1356,7 @@ namespace App {
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb) {
switch (document.type()) {
case mtpc_document: {
- const MTPDdocument &d(document.c_document());
+ const auto &d(document.c_document());
return App::documentSet(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v, StorageImageLocation());
} break;
case mtpc_documentEmpty: return App::document(document.c_documentEmpty().vid.v);
@@ -1450,12 +1444,12 @@ namespace App {
PeerData *peerByName(const QString &username) {
QString uname(username.trimmed());
- for (PeersData::const_iterator i = peersData.cbegin(), e = peersData.cend(); i != e; ++i) {
- if (!i.value()->userName().compare(uname, Qt::CaseInsensitive)) {
- return i.value();
+ for_const (PeerData *peer, peersData) {
+ if (!peer->userName().compare(uname, Qt::CaseInsensitive)) {
+ return peer;
}
}
- return 0;
+ return nullptr;
}
void updateImage(ImagePtr &old, ImagePtr now) {
@@ -1535,7 +1529,7 @@ namespace App {
DocumentData *document(const DocumentId &document) {
DocumentsData::const_iterator i = ::documentsData.constFind(document);
if (i == ::documentsData.cend()) {
- i = ::documentsData.insert(document, new DocumentData(document));
+ i = ::documentsData.insert(document, DocumentData::create(document));
}
return i.value();
}
@@ -1543,45 +1537,44 @@ namespace App {
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation) {
bool sentSticker = false;
if (convert) {
+ MediaKey oldKey = convert->mediaKey();
if (convert->id != document) {
DocumentsData::iterator i = ::documentsData.find(convert->id);
if (i != ::documentsData.cend() && i.value() == convert) {
::documentsData.erase(i);
}
- // inline bot sent gifs caching
- if (!convert->voice() && !convert->isVideo()) {
- Local::copyStickerImage(mediaKey(DocumentFileLocation, convert->dc, convert->id), mediaKey(DocumentFileLocation, dc, document));
- }
-
convert->id = document;
convert->status = FileReady;
sentSticker = (convert->sticker() != 0);
}
if (date) {
- convert->access = access;
- convert->date = date;
convert->setattributes(attributes);
+ convert->setRemoteLocation(dc, access);
+ convert->date = date;
convert->mime = mime;
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) {
updateImage(convert->thumb, thumb);
}
- convert->dc = dc;
convert->size = size;
convert->recountIsImage();
if (convert->sticker() && convert->sticker()->loc.isNull() && !thumbLocation.isNull()) {
convert->sticker()->loc = thumbLocation;
}
+
+ MediaKey newKey = convert->mediaKey();
+ if (newKey != oldKey) {
+ if (convert->voice()) {
+ Local::copyAudio(oldKey, newKey);
+ } else if (convert->sticker() || convert->isAnimation()) {
+ Local::copyStickerImage(oldKey, newKey);
+ }
+ }
}
if (cSavedGifs().indexOf(convert) >= 0) { // id changed
Local::writeSavedGifs();
}
-
- const FileLocation &loc(convert->location(true));
- if (!loc.isEmpty()) {
- Local::writeFileLocation(convert->mediaKey(), loc);
- }
}
DocumentsData::const_iterator i = ::documentsData.constFind(document);
DocumentData *result;
@@ -1589,7 +1582,11 @@ namespace App {
if (convert) {
result = convert;
} else {
- result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
+ result = DocumentData::create(document, dc, access, attributes);
+ result->date = date;
+ result->mime = mime;
+ result->thumb = thumb;
+ result->size = size;
result->recountIsImage();
if (result->sticker()) {
result->sticker()->loc = thumbLocation;
@@ -1599,14 +1596,15 @@ namespace App {
} else {
result = i.value();
if (result != convert && date) {
- result->access = access;
- result->date = date;
result->setattributes(attributes);
+ if (!result->isValid()) {
+ result->setRemoteLocation(dc, access);
+ }
+ result->date = date;
result->mime = mime;
if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
result->thumb = thumb;
}
- result->dc = dc;
result->size = size;
result->recountIsImage();
if (result->sticker() && result->sticker()->loc.isNull() && !thumbLocation.isNull()) {
@@ -1628,7 +1626,7 @@ namespace App {
return i.value();
}
- WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill) {
+ WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *document, int32 duration, const QString &author, int32 pendingTill) {
if (convert) {
if (convert->id != webPage) {
WebPagesData::iterator i = webPagesData.find(convert->id);
@@ -1645,7 +1643,7 @@ namespace App {
convert->title = title;
convert->description = description;
convert->photo = photo;
- convert->doc = doc;
+ convert->document = document;
convert->duration = duration;
convert->author = author;
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
@@ -1659,7 +1657,7 @@ namespace App {
if (convert) {
result = convert;
} else {
- result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, photo, doc, duration, author, (pendingTill >= -1) ? pendingTill : -1);
+ result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, document, photo, duration, author, (pendingTill >= -1) ? pendingTill : -1);
if (pendingTill > 0 && api()) {
api()->requestWebPageDelayed(result);
}
@@ -1676,7 +1674,7 @@ namespace App {
result->title = title;
result->description = description;
result->photo = photo;
- result->doc = doc;
+ result->document = document;
result->duration = duration;
result->author = author;
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
@@ -1712,7 +1710,7 @@ namespace App {
MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo) {
if (photo.type() == mtpc_userProfilePhoto) {
- const MTPDuserProfilePhoto &uphoto(photo.c_userProfilePhoto());
+ const auto &uphoto(photo.c_userProfilePhoto());
QVector photoSizes;
photoSizes.push_back(MTP_photoSize(MTP_string("a"), uphoto.vphoto_small, MTP_int(160), MTP_int(160), MTP_int(0)));
@@ -1744,14 +1742,16 @@ namespace App {
}
HistoryItem *histItemById(ChannelId channelId, MsgId itemId) {
- MsgsData *data = fetchMsgsData(channelId, false);
- if (!data) return 0;
+ if (!itemId) return nullptr;
- MsgsData::const_iterator i = data->constFind(itemId);
+ MsgsData *data = fetchMsgsData(channelId, false);
+ if (!data) return nullptr;
+
+ auto i = data->constFind(itemId);
if (i != data->cend()) {
return i.value();
}
- return 0;
+ return nullptr;
}
void historyRegItem(HistoryItem *item) {
@@ -1851,8 +1851,6 @@ namespace App {
}
::hoveredItem = ::pressedItem = ::hoveredLinkItem = ::pressedLinkItem = ::contextItem = 0;
- replyMarkups.clear();
- channelReplyMarkups.clear();
}
void historyClearItems() {
@@ -1863,20 +1861,20 @@ namespace App {
cSetSavedPeers(SavedPeers());
cSetSavedPeersByTime(SavedPeersByTime());
cSetRecentInlineBots(RecentInlineBots());
- for (PeersData::const_iterator i = peersData.cbegin(), e = peersData.cend(); i != e; ++i) {
- delete *i;
+ for_const (PeerData *peer, peersData) {
+ delete peer;
}
peersData.clear();
- for (PhotosData::const_iterator i = ::photosData.cbegin(), e = ::photosData.cend(); i != e; ++i) {
- delete *i;
+ for_const (PhotoData *photo, ::photosData) {
+ delete photo;
}
::photosData.clear();
- for (DocumentsData::const_iterator i = ::documentsData.cbegin(), e = ::documentsData.cend(); i != e; ++i) {
- delete *i;
+ for_const (DocumentData *document, ::documentsData) {
+ delete document;
}
::documentsData.clear();
- for (WebPagesData::const_iterator i = webPagesData.cbegin(), e = webPagesData.cend(); i != e; ++i) {
- delete *i;
+ for_const (WebPageData *webpage, webPagesData) {
+ delete webpage;
}
webPagesData.clear();
if (api()) api()->clearWebPageRequests();
@@ -2020,6 +2018,7 @@ namespace App {
::cornersMask[i]->setDevicePixelRatio(cRetinaFactor());
}
prepareCorners(BlackCorners, st::msgRadius, st::black);
+ prepareCorners(WhiteCorners, st::msgRadius, st::white);
prepareCorners(ServiceCorners, st::msgRadius, st::msgServiceBg);
prepareCorners(ServiceSelectedCorners, st::msgRadius, st::msgServiceSelectBg);
prepareCorners(SelectedOverlayCorners, st::msgRadius, st::msgSelectOverlay);
@@ -2048,8 +2047,8 @@ namespace App {
}
void clearHistories() {
- textlnkOver(TextLinkPtr());
- textlnkDown(TextLinkPtr());
+ ClickHandler::clearActive();
+ ClickHandler::unpressed();
histories().clear();
@@ -2083,6 +2082,8 @@ namespace App {
mainEmojiMap.clear();
otherEmojiMap.clear();
+ Dialogs::Layout::clearStyleSheets();
+
clearAllImages();
}
@@ -2343,7 +2344,7 @@ namespace App {
GifItems gifs = ::gifItems;
for (GifItems::const_iterator i = gifs.cbegin(), e = gifs.cend(); i != e; ++i) {
if (HistoryMedia *media = i.value()->getMedia()) {
- media->stopInline(i.value());
+ media->stopInline();
}
}
}
@@ -2388,106 +2389,6 @@ namespace App {
if (changeInMin) App::main()->updateMutedIn(changeInMin);
}
- void regInlineResultLoader(FileLoader *loader, InlineResult *result) {
- ::inlineResultLoaders.insert(loader, result);
- }
-
- void unregInlineResultLoader(FileLoader *loader) {
- ::inlineResultLoaders.remove(loader);
- }
-
- InlineResult *inlineResultFromLoader(FileLoader *loader) {
- InlineResultLoaders::const_iterator i = ::inlineResultLoaders.find(loader);
- return (i == ::inlineResultLoaders.cend()) ? 0 : i.value();
- }
-
- inline void insertReplyMarkup(ChannelId channelId, MsgId msgId, const ReplyMarkup &markup) {
- if (channelId == NoChannel) {
- replyMarkups.insert(msgId, markup);
- } else {
- channelReplyMarkups[channelId].insert(msgId, markup);
- }
- }
-
- void feedReplyMarkup(ChannelId channelId, MsgId msgId, const MTPReplyMarkup &markup) {
- ReplyMarkup data;
- ReplyMarkup::Commands &commands(data.commands);
- switch (markup.type()) {
- case mtpc_replyKeyboardMarkup: {
- const MTPDreplyKeyboardMarkup &d(markup.c_replyKeyboardMarkup());
- data.flags = d.vflags.v;
-
- const QVector &v(d.vrows.c_vector().v);
- if (!v.isEmpty()) {
- commands.reserve(v.size());
- for (int32 i = 0, l = v.size(); i < l; ++i) {
- switch (v.at(i).type()) {
- case mtpc_keyboardButtonRow: {
- const MTPDkeyboardButtonRow &r(v.at(i).c_keyboardButtonRow());
- const QVector &b(r.vbuttons.c_vector().v);
- if (!b.isEmpty()) {
- QList btns;
- btns.reserve(b.size());
- for (int32 j = 0, s = b.size(); j < s; ++j) {
- switch (b.at(j).type()) {
- case mtpc_keyboardButton: {
- btns.push_back(qs(b.at(j).c_keyboardButton().vtext));
- } break;
- }
- }
- if (!btns.isEmpty()) commands.push_back(btns);
- }
- } break;
- }
- }
- if (!commands.isEmpty()) {
- insertReplyMarkup(channelId, msgId, data);
- }
- }
- } break;
-
- case mtpc_replyKeyboardHide: {
- const MTPDreplyKeyboardHide &d(markup.c_replyKeyboardHide());
- if (d.vflags.v) {
- insertReplyMarkup(channelId, msgId, ReplyMarkup(mtpCastFlags(d.vflags.v) | MTPDreplyKeyboardMarkup_ClientFlag::f_zero));
- }
- } break;
-
- case mtpc_replyKeyboardForceReply: {
- const MTPDreplyKeyboardForceReply &d(markup.c_replyKeyboardForceReply());
- insertReplyMarkup(channelId, msgId, ReplyMarkup(mtpCastFlags(d.vflags.v) | MTPDreplyKeyboardMarkup_ClientFlag::f_force_reply));
- } break;
- }
- }
-
- void clearReplyMarkup(ChannelId channelId, MsgId msgId) {
- if (channelId == NoChannel) {
- replyMarkups.remove(msgId);
- } else {
- ChannelReplyMarkups::iterator i = channelReplyMarkups.find(channelId);
- if (i != channelReplyMarkups.cend()) {
- i->remove(msgId);
- if (i->isEmpty()) {
- channelReplyMarkups.erase(i);
- }
- }
- }
- }
-
- inline const ReplyMarkup &replyMarkup(const ReplyMarkups &markups, MsgId msgId) {
- ReplyMarkups::const_iterator i = markups.constFind(msgId);
- if (i == markups.cend()) return zeroMarkup;
- return i.value();
- }
- const ReplyMarkup &replyMarkup(ChannelId channelId, MsgId msgId) {
- if (channelId == NoChannel) {
- return replyMarkup(replyMarkups, msgId);
- }
- ChannelReplyMarkups::const_iterator j = channelReplyMarkups.constFind(channelId);
- if (j == channelReplyMarkups.cend()) return zeroMarkup;
- return replyMarkup(*j, msgId);
- }
-
void setProxySettings(QNetworkAccessManager &manager) {
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
manager.setProxy(getHttpProxySettings());
diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h
index ebe1711e5..4e437b71b 100644
--- a/Telegram/SourceFiles/app.h
+++ b/Telegram/SourceFiles/app.h
@@ -20,10 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
-#include "types.h"
+#include "core/basic_types.h"
class AppClass;
-class Window;
+class MainWindow;
class MainWidget;
class SettingsWidget;
class ApiWrap;
@@ -44,19 +44,11 @@ typedef QHash GifItems;
typedef QHash PhotosData;
typedef QHash DocumentsData;
-struct ReplyMarkup {
- ReplyMarkup(MTPDreplyKeyboardMarkup::Flags flags = 0) : flags(flags) {
- }
- typedef QList > Commands;
- Commands commands;
- MTPDreplyKeyboardMarkup::Flags flags;
-};
-
class LayeredWidget;
namespace App {
AppClass *app();
- Window *wnd();
+ MainWindow *wnd();
MainWidget *main();
SettingsWidget *settings();
bool passcoded();
@@ -64,7 +56,6 @@ namespace App {
ApiWrap *api();
void logOut();
- bool loggedOut();
QString formatPhone(QString phone);
@@ -268,14 +259,6 @@ namespace App {
void unregMuted(PeerData *peer);
void updateMuted();
- void regInlineResultLoader(FileLoader *loader, InlineResult *result);
- void unregInlineResultLoader(FileLoader *loader);
- InlineResult *inlineResultFromLoader(FileLoader *loader);
-
- void feedReplyMarkup(ChannelId channelId, MsgId msgId, const MTPReplyMarkup &markup);
- void clearReplyMarkup(ChannelId channelId, MsgId msgId);
- const ReplyMarkup &replyMarkup(ChannelId channelId, MsgId msgId);
-
void setProxySettings(QNetworkAccessManager &manager);
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
QNetworkProxy getHttpProxySettings();
diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp
index f84770840..a5d4a7334 100644
--- a/Telegram/SourceFiles/application.cpp
+++ b/Telegram/SourceFiles/application.cpp
@@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "application.h"
-#include "style.h"
+#include "ui/style.h"
#include "shortcuts.h"
@@ -690,7 +690,7 @@ AppClass::AppClass() : QObject()
cSetLang(languageDefault);
}
} else if (cLang() > languageDefault && cLang() < languageCount) {
- LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()] + qsl(".strings"));
+ LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings"));
if (!loader.errors().isEmpty()) {
LOG(("Lang load errors: %1").arg(loader.errors()));
} else if (!loader.warnings().isEmpty()) {
@@ -718,7 +718,7 @@ AppClass::AppClass() : QObject()
QMimeDatabase().mimeTypeForName(qsl("text/plain")); // create mime database
- _window = new Window();
+ _window = new MainWindow();
_window->createWinId();
_window->init();
@@ -835,7 +835,7 @@ void AppClass::chatPhotoCleared(PeerId peer, const MTPUpdates &updates) {
void AppClass::selfPhotoDone(const MTPphotos_Photo &result) {
if (!App::self()) return;
- const MTPDphotos_photo &photo(result.c_photos_photo());
+ const auto &photo(result.c_photos_photo());
App::feedPhoto(photo.vphoto);
App::feedUsers(photo.vusers);
cancelPhotoUpdate(App::self()->id);
@@ -851,7 +851,7 @@ void AppClass::chatPhotoDone(PeerId peer, const MTPUpdates &updates) {
}
bool AppClass::peerPhotoFail(PeerId peer, const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
LOG(("Application Error: update photo failed %1: %2").arg(error.type()).arg(error.description()));
cancelPhotoUpdate(peer);
@@ -962,6 +962,15 @@ void AppClass::onSwitchDebugMode() {
}
}
+void AppClass::onSwitchWorkMode() {
+ Global::SetDialogsModeEnabled(!Global::DialogsModeEnabled());
+ Global::SetDialogsMode(Dialogs::Mode::All);
+ Local::writeUserSettings();
+ cSetRestarting(true);
+ cSetRestartingToSettings(true);
+ App::quit();
+}
+
void AppClass::onSwitchTestMode() {
if (cTestMode()) {
QFile(cWorkingDir() + qsl("tdata/withtestmode")).remove();
@@ -1025,12 +1034,11 @@ void AppClass::checkMapVersion() {
if (Local::oldMapVersion() < AppVersion) {
if (Local::oldMapVersion()) {
QString versionFeatures;
- if ((cDevVersion() || cBetaVersion()) && Local::oldMapVersion() < 9035) {
-// QString ctrl = (cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? qsl("Cmd") : qsl("Ctrl");
- versionFeatures = QString::fromUtf8("\xe2\x80\x94 Design improvements\n\xe2\x80\x94 Bug fixes and other minor improvements");// .replace('@', qsl("@") + QChar(0x200D));
-// versionFeatures = lng_new_version_text(lt_link, qsl("https://telegram.org/blog/supergroups5k")).trimmed();
- } else if (Local::oldMapVersion() < 9031) {
- versionFeatures = lng_new_version_text(lt_link, qsl("https://telegram.org/blog/supergroups5k")).trimmed();
+ if ((cDevVersion() || cBetaVersion()) && Local::oldMapVersion() < 9041) {
+// versionFeatures = QString::fromUtf8("\xe2\x80\x94 Design improvements\n\xe2\x80\x94 Bug fixes and other minor improvements");
+ versionFeatures = langNewVersionText();
+ } else if (Local::oldMapVersion() < 9041) {
+ versionFeatures = langNewVersionText();
} else {
versionFeatures = lang(lng_new_version_minor).trimmed();
}
@@ -1040,15 +1048,12 @@ void AppClass::checkMapVersion() {
}
}
}
- if (cNeedConfigResave()) {
- Local::writeUserSettings();
- }
}
AppClass::~AppClass() {
Shortcuts::finish();
- if (Window *w = _window) {
+ if (auto w = _window) {
_window = 0;
delete w;
}
@@ -1081,7 +1086,7 @@ AppClass *AppClass::app() {
return AppObject;
}
-Window *AppClass::wnd() {
+MainWindow *AppClass::wnd() {
return AppObject ? AppObject->_window : 0;
}
diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h
index 3e0a37e56..774ebc85b 100644
--- a/Telegram/SourceFiles/application.h
+++ b/Telegram/SourceFiles/application.h
@@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
-#include "window.h"
+#include "mainwindow.h"
#include "pspecific.h"
class UpdateChecker;
@@ -153,7 +153,7 @@ public:
~AppClass();
static AppClass *app();
- static Window *wnd();
+ static MainWindow *wnd();
static MainWidget *main();
FileUploader *uploader();
@@ -195,6 +195,7 @@ public slots:
void photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file);
void onSwitchDebugMode();
+ void onSwitchWorkMode();
void onSwitchTestMode();
void killDownloadSessions();
@@ -211,7 +212,7 @@ private:
uint64 _lastActionTime;
- Window *_window;
+ MainWindow *_window;
FileUploader *_uploader;
Translator *_translator;
diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp
index 29205c7af..49a536ed1 100644
--- a/Telegram/SourceFiles/audio.cpp
+++ b/Telegram/SourceFiles/audio.cpp
@@ -506,7 +506,7 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) {
if (current->file.isEmpty() && current->data.isEmpty()) {
setStoppedState(current);
if (!song.song->loading()) {
- DocumentOpenLink::doOpen(song.song);
+ DocumentOpenClickHandler::doOpen(song.song);
}
} else {
current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying;
diff --git a/Telegram/SourceFiles/audio.h b/Telegram/SourceFiles/audio.h
index f9259906e..17bbf8cf0 100644
--- a/Telegram/SourceFiles/audio.h
+++ b/Telegram/SourceFiles/audio.h
@@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
-#include "types.h"
+#include "core/basic_types.h"
void audioInit();
bool audioWorks();
diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp
index 87e01f6a3..083d6b650 100644
--- a/Telegram/SourceFiles/boxes/aboutbox.cpp
+++ b/Telegram/SourceFiles/boxes/aboutbox.cpp
@@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "aboutbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
#include "autoupdater.h"
#include "boxes/confirmbox.h"
@@ -139,7 +139,7 @@ void AboutBox::dropEvent(QDropEvent *e) {
QString telegramFaqLink() {
QString result = qsl("https://telegram.org/faq");
if (cLang() > languageDefault && cLang() < languageCount) {
- const char *code = LanguageCodes[cLang()];
+ const char *code = LanguageCodes[cLang()].c_str();
if (qstr("de") == code || qstr("es") == code || qstr("it") == code || qstr("ko") == code) {
result += qsl("/") + code;
} else if (qstr("pt_BR") == code) {
diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp
index e9d1783ad..f721c6b57 100644
--- a/Telegram/SourceFiles/boxes/abstractbox.cpp
+++ b/Telegram/SourceFiles/boxes/abstractbox.cpp
@@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "abstractbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
void BlueTitleShadow::paintEvent(QPaintEvent *e) {
Painter p(this);
diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp
index 53d9e5d80..444742a81 100644
--- a/Telegram/SourceFiles/boxes/addcontactbox.cpp
+++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp
@@ -26,9 +26,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "contactsbox.h"
#include "confirmbox.h"
#include "photocropbox.h"
-#include "gui/filedialog.h"
+#include "ui/filedialog.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
+#include "apiwrap.h"
AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth)
, _user(0)
@@ -199,7 +200,7 @@ void AddContactBox::onSave() {
}
bool AddContactBox::onSaveUserFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_addRequest = 0;
QString err(error.type());
@@ -220,13 +221,13 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
if (isHidden() || !App::main()) return;
- const MTPDcontacts_importedContacts &d(res.c_contacts_importedContacts());
+ const auto &d(res.c_contacts_importedContacts());
App::feedUsers(d.vusers);
- const QVector &v(d.vimported.c_vector().v);
+ const auto &v(d.vimported.c_vector().v);
UserData *user = nullptr;
if (!v.isEmpty()) {
- const MTPDimportedContact &c(v.front().c_importedContact());
+ const auto &c(v.front().c_importedContact());
if (c.vclient_id.v != _contactId) return;
user = App::userLoaded(c.vuser_id.v);
@@ -246,7 +247,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
}
void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
- const MTPDcontacts_importedContacts &d(res.c_contacts_importedContacts());
+ const auto &d(res.c_contacts_importedContacts());
App::feedUsers(d.vusers);
emit closed();
}
@@ -530,7 +531,7 @@ void GroupInfoBox::creationDone(const MTPUpdates &updates) {
}
bool GroupInfoBox::creationFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_creationRequestId = 0;
if (error.type() == "NO_CHAT_TITLE") {
@@ -903,7 +904,7 @@ void SetupChannelBox::onUpdateDone(const MTPBool &result) {
}
bool SetupChannelBox::onUpdateFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_saveRequestId = 0;
QString err(error.type());
@@ -940,7 +941,7 @@ void SetupChannelBox::onCheckDone(const MTPBool &result) {
}
bool SetupChannelBox::onCheckFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_checkRequestId = 0;
QString err(error.type());
@@ -971,7 +972,7 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) {
}
bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_checkRequestId = 0;
QString err(error.type());
@@ -1128,7 +1129,7 @@ void EditNameTitleBox::onSaveSelfDone(const MTPUser &user) {
}
bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
QString err(error.type());
QString first = textOneLine(_first.getLastText().trimmed()), last = textOneLine(_last.getLastText().trimmed());
@@ -1150,7 +1151,7 @@ bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) {
}
bool EditNameTitleBox::onSaveChatFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_requestId = 0;
QString err(error.type());
@@ -1183,7 +1184,7 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox()
, _saveTitleRequestId(0)
, _saveDescriptionRequestId(0)
, _saveSignRequestId(0) {
- connect(App::main(), SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*)));
+ connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*)));
setMouseTracking(true);
@@ -1334,7 +1335,7 @@ void EditChannelBox::saveSign() {
}
bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
QString err(error.type());
if (req == _saveTitleRequestId) {
diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp
index 3af3b2615..a755697ca 100644
--- a/Telegram/SourceFiles/boxes/autolockbox.cpp
+++ b/Telegram/SourceFiles/boxes/autolockbox.cpp
@@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "autolockbox.h"
#include "confirmbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
AutoLockBox::AutoLockBox() :
_close(this, lang(lng_box_ok), st::defaultBoxButton) {
diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp
index e35eb32c9..ddbdf4a39 100644
--- a/Telegram/SourceFiles/boxes/backgroundbox.cpp
+++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp
@@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "backgroundbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
#include "settingswidget.h"
BackgroundInner::BackgroundInner() :
@@ -42,13 +42,13 @@ void BackgroundInner::gotWallpapers(const MTPVector &result) {
App::WallPapers wallpapers;
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0)));
- const QVector &v(result.c_vector().v);
+ const auto &v(result.c_vector().v);
for (int i = 0, l = v.size(); i < l; ++i) {
- const MTPWallPaper w(v.at(i));
+ const auto &w(v.at(i));
switch (w.type()) {
case mtpc_wallPaper: {
- const MTPDwallPaper &d(w.c_wallPaper());
- const QVector &sizes(d.vsizes.c_vector().v);
+ const auto &d(w.c_wallPaper());
+ const auto &sizes(d.vsizes.c_vector().v);
const MTPPhotoSize *thumb = 0, *full = 0;
int32 thumbLevel = -1, fullLevel = -1;
for (QVector::const_iterator j = sizes.cbegin(), e = sizes.cend(); j != e; ++j) {
@@ -56,14 +56,14 @@ void BackgroundInner::gotWallpapers(const MTPVector &result) {
int32 w = 0, h = 0;
switch (j->type()) {
case mtpc_photoSize: {
- const string &s(j->c_photoSize().vtype.c_string().v);
+ const auto &s(j->c_photoSize().vtype.c_string().v);
if (s.size()) size = s[0];
w = j->c_photoSize().vw.v;
h = j->c_photoSize().vh.v;
} break;
case mtpc_photoCachedSize: {
- const string &s(j->c_photoCachedSize().vtype.c_string().v);
+ const auto &s(j->c_photoCachedSize().vtype.c_string().v);
if (s.size()) size = s[0];
w = j->c_photoCachedSize().vw.v;
h = j->c_photoCachedSize().vh.v;
@@ -87,7 +87,7 @@ void BackgroundInner::gotWallpapers(const MTPVector &result) {
} break;
case mtpc_wallPaperSolid: {
- const MTPDwallPaperSolid &d(w.c_wallPaperSolid());
+ const auto &d(w.c_wallPaperSolid());
} break;
}
}
diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp
index 3e379aae0..910243ea0 100644
--- a/Telegram/SourceFiles/boxes/confirmbox.cpp
+++ b/Telegram/SourceFiles/boxes/confirmbox.cpp
@@ -23,9 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "confirmbox.h"
#include "mainwidget.h"
-#include "window.h"
-
+#include "mainwindow.h"
+#include "apiwrap.h"
#include "application.h"
+#include "core/click_handler_types.h"
TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseRichText, // flags
@@ -83,33 +84,30 @@ void ConfirmBox::mouseMoveEvent(QMouseEvent *e) {
void ConfirmBox::mousePressEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateHover();
- if (textlnkOver()) {
- textlnkDown(textlnkOver());
- update();
- }
+ ClickHandler::pressed();
return LayeredWidget::mousePressEvent(e);
}
void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateHover();
- if (textlnkOver() && textlnkOver() == textlnkDown()) {
+ if (ClickHandlerPtr activated = ClickHandler::unpressed()) {
Ui::hideLayer();
- textlnkOver()->onClick(e->button());
+ App::activateClickHandler(activated, e->button());
}
- textlnkDown(TextLinkPtr());
}
void ConfirmBox::leaveEvent(QEvent *e) {
- if (_myLink) {
- if (textlnkOver() == _myLink) {
- textlnkOver(TextLinkPtr());
- update();
- }
- _myLink = TextLinkPtr();
- setCursor(style::cur_default);
- update();
- }
+ ClickHandler::clearActive(this);
+}
+
+void ConfirmBox::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
+ setCursor(active ? style::cur_pointer : style::cur_default);
+ update();
+}
+
+void ConfirmBox::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
+ update();
}
void ConfirmBox::updateLink() {
@@ -119,17 +117,12 @@ void ConfirmBox::updateLink() {
void ConfirmBox::updateHover() {
QPoint m(mapFromGlobal(_lastMousePos));
- bool wasMy = (_myLink == textlnkOver());
+
textstyleSet(&st::boxTextStyle);
- _myLink = _text.linkLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width(), (_text.maxWidth() < width()) ? style::al_center : style::al_left);
+ auto state = _text.getStateLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width());
textstyleRestore();
- if (_myLink != textlnkOver()) {
- if (wasMy || _myLink || rect().contains(m)) {
- textlnkOver(_myLink);
- }
- setCursor(_myLink ? style::cur_pointer : style::cur_default);
- update();
- }
+
+ ClickHandler::setActive(state.link, this);
}
void ConfirmBox::closePressed() {
@@ -174,6 +167,16 @@ void ConfirmBox::resizeEvent(QResizeEvent *e) {
_cancel.moveToRight(st::boxButtonPadding.right() + _confirm.width() + st::boxButtonPadding.left(), _confirm.y());
}
+SharePhoneConfirmBox::SharePhoneConfirmBox(PeerData *recipient)
+: ConfirmBox(lang(lng_bot_share_phone), lang(lng_bot_share_phone_confirm))
+, _recipient(recipient) {
+ connect(this, SIGNAL(confirmed()), this, SLOT(onConfirm()));
+}
+
+void SharePhoneConfirmBox::onConfirm() {
+ emit confirmed(_recipient);
+}
+
ConfirmLinkBox::ConfirmLinkBox(const QString &url) : ConfirmBox(lang(lng_open_this_link) + qsl("\n\n") + url, lang(lng_open_link))
, _url(url) {
connect(this, SIGNAL(confirmed()), this, SLOT(onOpenLink()));
@@ -181,11 +184,7 @@ ConfirmLinkBox::ConfirmLinkBox(const QString &url) : ConfirmBox(lang(lng_open_th
void ConfirmLinkBox::onOpenLink() {
Ui::hideLayer();
- if (reMailStart().match(_url).hasMatch()) {
- EmailLink(_url).onClick(Qt::LeftButton);
- } else {
- TextLink(_url).onClick(Qt::LeftButton);
- }
+ UrlClickHandler::doOpen(_url);
}
MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth)
@@ -337,7 +336,7 @@ void ConvertToSupergroupBox::convertDone(const MTPUpdates &updates) {
}
bool ConvertToSupergroupBox::convertFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
Ui::hideLayer();
return true;
}
@@ -433,7 +432,7 @@ void PinMessageBox::pinDone(const MTPUpdates &updates) {
}
bool PinMessageBox::pinFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
Ui::hideLayer();
return true;
}
diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h
index 05b8febf3..097e6f436 100644
--- a/Telegram/SourceFiles/boxes/confirmbox.h
+++ b/Telegram/SourceFiles/boxes/confirmbox.h
@@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "abstractbox.h"
class InformBox;
-class ConfirmBox : public AbstractBox {
+class ConfirmBox : public AbstractBox, public ClickHandlerHost {
Q_OBJECT
public:
@@ -38,6 +38,10 @@ public:
void leaveEvent(QEvent *e);
void updateLink();
+ // ClickHandlerHost interface
+ void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active);
+ void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed);
+
public slots:
void onCancel();
@@ -69,7 +73,6 @@ private:
void updateHover();
QPoint _lastMousePos;
- TextLinkPtr _myLink;
BoxButton _confirm, _cancel;
};
@@ -80,6 +83,23 @@ public:
}
};
+class SharePhoneConfirmBox : public ConfirmBox {
+ Q_OBJECT
+
+public:
+ SharePhoneConfirmBox(PeerData *recipient);
+
+signals:
+ void confirmed(PeerData *recipient);
+
+private slots:
+ void onConfirm();
+
+private:
+ PeerData *_recipient;
+
+};
+
class ConfirmLinkBox : public ConfirmBox {
Q_OBJECT
diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp
index aa5ebd767..8f2fc96b0 100644
--- a/Telegram/SourceFiles/boxes/connectionbox.cpp
+++ b/Telegram/SourceFiles/boxes/connectionbox.cpp
@@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "connectionbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth)
, _hostInput(this, st::connectionHostInputField, lang(lng_connection_host_ph), cConnectionProxy().host)
@@ -336,7 +336,6 @@ void AutoDownloadBox::onSave() {
i.value()->automaticLoadSettingsChanged();
}
}
- Notify::automaticLoadSettingsChangedGif();
}
changed = true;
}
diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp
index 3e0cf9212..326c94bef 100644
--- a/Telegram/SourceFiles/boxes/contactsbox.cpp
+++ b/Telegram/SourceFiles/boxes/contactsbox.cpp
@@ -19,70 +19,43 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
+#include "boxes/contactsbox.h"
+
+#include "dialogs/dialogs_indexed_list.h"
#include "lang.h"
-
-#include "addcontactbox.h"
-#include "contactsbox.h"
+#include "boxes/addcontactbox.h"
+#include "boxes/contactsbox.h"
#include "mainwidget.h"
-#include "window.h"
-
+#include "mainwindow.h"
#include "application.h"
+#include "ui/filedialog.h"
+#include "boxes/photocropbox.h"
+#include "boxes/confirmbox.h"
+#include "apiwrap.h"
-#include "gui/filedialog.h"
-#include "photocropbox.h"
-
-#include "confirmbox.h"
+QString cantInviteError() {
+ return lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.me/spambot"), lang(lng_cant_more_info)));
+}
ContactsInner::ContactsInner(CreatingGroupType creating) : TWidget()
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _newItemHeight(creating == CreatingGroupNone ? st::contactsNewItemHeight : 0)
-, _newItemSel(false)
-, _chat(0)
-, _channel(0)
-, _membersFilter(MembersFilterRecent)
-, _bot(0)
, _creating(creating)
, _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox)
-, _addToPeer(0)
-, _addAdmin(0)
-, _addAdminRequestId(0)
-, _addAdminBox(0)
-, _contacts(&App::main()->contactsList())
-, _sel(0)
-, _filteredSel(-1)
-, _mouseSel(false)
-, _selCount(0)
-, _searching(false)
-, _byUsernameSel(-1)
-, _addContactLnk(this, lang(lng_add_contact_button))
-, _saving(false) {
+, _contacts(App::main()->contactsList())
+, _addContactLnk(this, lang(lng_add_contact_button)) {
init();
}
ContactsInner::ContactsInner(ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : TWidget()
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
-, _newItemHeight(0)
-, _newItemSel(false)
-, _chat(0)
, _channel(channel)
, _membersFilter(membersFilter)
-, _bot(0)
, _creating(CreatingGroupChannel)
, _already(already)
, _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox)
-, _addToPeer(0)
-, _addAdmin(0)
-, _addAdminRequestId(0)
-, _addAdminBox(0)
-, _contacts(&App::main()->contactsList())
-, _sel(0)
-, _filteredSel(-1)
-, _mouseSel(false)
-, _selCount(0)
-, _searching(false)
-, _byUsernameSel(-1)
-, _addContactLnk(this, lang(lng_add_contact_button))
-, _saving(false) {
+, _contacts(App::main()->contactsList())
+, _addContactLnk(this, lang(lng_add_contact_button)) {
init();
}
@@ -94,34 +67,19 @@ namespace {
ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWidget()
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
-, _newItemHeight(0)
-, _newItemSel(false)
, _chat(chat)
-, _channel(0)
, _membersFilter(membersFilter)
-, _bot(0)
-, _creating(CreatingGroupNone)
, _allAdmins(this, lang(lng_chat_all_members_admins), !_chat->adminsEnabled(), st::contactsAdminCheckbox)
, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right() - st::contactsCheckPosition.x() * 2 - st::contactsCheckIcon.pxWidth())
, _aboutAllAdmins(st::boxTextFont, lang(lng_chat_about_all_admins), _defaultOptions, _aboutWidth)
, _aboutAdmins(st::boxTextFont, lang(lng_chat_about_admins), _defaultOptions, _aboutWidth)
-, _addToPeer(0)
-, _addAdmin(0)
-, _addAdminRequestId(0)
-, _addAdminBox(0)
-, _contacts((membersFilter == MembersFilterRecent) ? (&App::main()->contactsList()) : (new DialogsIndexed(DialogsSortByAdd)))
-, _sel(0)
-, _filteredSel(-1)
-, _mouseSel(false)
-, _selCount(0)
-, _searching(false)
-, _byUsernameSel(-1)
-, _addContactLnk(this, lang(lng_add_contact_button))
-, _saving(false) {
+, _customList((membersFilter == MembersFilterRecent) ? std_::unique_ptr() : std_::make_unique(Dialogs::SortMode::Add))
+, _contacts((membersFilter == MembersFilterRecent) ? App::main()->contactsList() : _customList.get())
+, _addContactLnk(this, lang(lng_add_contact_button)) {
initList();
if (membersFilter == MembersFilterAdmins) {
_newItemHeight = st::contactsNewItemHeight + qMax(_aboutAllAdmins.countHeight(_aboutWidth), _aboutAdmins.countHeight(_aboutWidth)) + st::contactsAboutHeight;
- if (!_contacts->list.count) {
+ if (_contacts->isEmpty()) {
App::api()->requestFullPeer(_chat);
}
}
@@ -130,33 +88,18 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid
ContactsInner::ContactsInner(UserData *bot) : TWidget()
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
-, _newItemHeight(0)
-, _newItemSel(false)
-, _chat(0)
-, _channel(0)
-, _membersFilter(MembersFilterRecent)
, _bot(bot)
-, _creating(CreatingGroupNone)
, _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox)
-, _addToPeer(0)
-, _addAdmin(0)
-, _addAdminRequestId(0)
-, _addAdminBox(0)
-, _contacts(new DialogsIndexed(DialogsSortByAdd))
-, _sel(0)
-, _filteredSel(-1)
-, _mouseSel(false)
-, _selCount(0)
-, _searching(false)
-, _byUsernameSel(-1)
-, _addContactLnk(this, lang(lng_add_contact_button))
-, _saving(false) {
- DialogsIndexed &v(App::main()->dialogsList());
- for (DialogRow *r = v.list.begin; r != v.list.end; r = r->next) {
- if (r->history->peer->isChat() && r->history->peer->asChat()->canEdit()) {
- _contacts->addToEnd(r->history);
- } else if (r->history->peer->isMegagroup() && (r->history->peer->asChannel()->amCreator() || r->history->peer->asChannel()->amEditor())) {
- _contacts->addToEnd(r->history);
+, _customList(std_::make_unique(Dialogs::SortMode::Add))
+, _contacts(_customList.get())
+, _addContactLnk(this, lang(lng_add_contact_button)) {
+ auto v = App::main()->dialogsList();
+ for_const (auto row, *v) {
+ auto peer = row->history()->peer;
+ if (peer->isChat() && peer->asChat()->canEdit()) {
+ _contacts->addToEnd(row->history());
+ } else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) {
+ _contacts->addToEnd(row->history());
}
}
init();
@@ -169,14 +112,14 @@ void ContactsInner::init() {
setAttribute(Qt::WA_OpaquePaintEvent);
- for (DialogRow *r = _contacts->list.begin; r != _contacts->list.end; r = r->next) {
- r->attached = 0;
+ for_const (auto row, _contacts->all()) {
+ row->attached = nullptr;
}
_filter = qsl("a");
updateFilter();
- connect(App::main(), SIGNAL(dialogRowReplaced(DialogRow*,DialogRow*)), this, SLOT(onDialogRowReplaced(DialogRow*,DialogRow*)));
+ connect(App::main(), SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*)));
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData *)));
connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
@@ -274,7 +217,7 @@ void ContactsInner::addAdminDone(const MTPUpdates &result, mtpRequestId req) {
}
bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (req != _addAdminRequestId) return true;
@@ -301,7 +244,7 @@ void ContactsInner::saving(bool flag) {
void ContactsInner::peerUpdated(PeerData *peer) {
if (_chat && (!peer || peer == _chat)) {
bool inited = false;
- if (_membersFilter == MembersFilterAdmins && !_contacts->list.count && !_chat->participants.isEmpty()) {
+ if (_membersFilter == MembersFilterAdmins && _contacts->isEmpty() && !_chat->participants.isEmpty()) {
initList();
inited = true;
}
@@ -312,8 +255,8 @@ void ContactsInner::peerUpdated(PeerData *peer) {
delete i.value();
}
_contactsData.clear();
- for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
- row->attached = 0;
+ for_const (auto row, _contacts->all()) {
+ row->attached = nullptr;
}
if (!_filter.isEmpty()) {
for (int32 j = 0, s = _filtered.size(); j < s; ++j) {
@@ -329,10 +272,10 @@ void ContactsInner::peerUpdated(PeerData *peer) {
} else {
ContactsData::iterator i = _contactsData.find(peer);
if (i != _contactsData.cend()) {
- for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
+ for_const (auto row, _contacts->all()) {
if (row->attached == i.value()) {
- row->attached = 0;
- update(0, _newItemHeight + _rowHeight * row->pos, width(), _rowHeight);
+ row->attached = nullptr;
+ update(0, _newItemHeight + _rowHeight * row->pos(), width(), _rowHeight);
}
}
if (!_filter.isEmpty()) {
@@ -357,14 +300,13 @@ void ContactsInner::loadProfilePhotos(int32 yFrom) {
if (yFrom < 0) yFrom = 0;
if (_filter.isEmpty()) {
- if (_contacts->list.count) {
- _contacts->list.adjustCurrent(yFrom - _newItemHeight, _rowHeight);
- for (
- DialogRow *preloadFrom = _contacts->list.current;
- preloadFrom != _contacts->list.end && (_newItemHeight + preloadFrom->pos * _rowHeight) < yTo;
- preloadFrom = preloadFrom->next
- ) {
- preloadFrom->history->peer->loadUserpic();
+ if (!_contacts->isEmpty()) {
+ auto i = _contacts->cfind(yFrom - _newItemHeight, _rowHeight);
+ for (auto end = _contacts->cend(); i != end; ++i) {
+ if ((_newItemHeight + (*i)->pos() * _rowHeight) >= yTo) {
+ break;
+ }
+ (*i)->history()->peer->loadUserpic();
}
}
} else if (!_filtered.isEmpty()) {
@@ -375,16 +317,16 @@ void ContactsInner::loadProfilePhotos(int32 yFrom) {
if (to > _filtered.size()) to = _filtered.size();
for (; from < to; ++from) {
- _filtered[from]->history->peer->loadUserpic();
+ _filtered[from]->history()->peer->loadUserpic();
}
}
}
}
-ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) {
+ContactsInner::ContactData *ContactsInner::contactData(Dialogs::Row *row) {
ContactData *data = (ContactData*)row->attached;
if (!data) {
- PeerData *peer = row->history->peer;
+ PeerData *peer = row->history()->peer;
ContactsData::const_iterator i = _contactsData.constFind(peer);
if (i == _contactsData.cend()) {
_contactsData.insert(peer, data = new ContactData());
@@ -509,7 +451,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
int32 yFrom = r.y(), yTo = r.y() + r.height();
if (_filter.isEmpty()) {
- if (_contacts->list.count || !_byUsername.isEmpty()) {
+ if (!_contacts->isEmpty() || !_byUsername.isEmpty()) {
if (_newItemHeight) {
if (_chat) {
p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg);
@@ -530,18 +472,18 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
yTo -= _newItemHeight;
p.translate(0, _newItemHeight);
}
- if (_contacts->list.count) {
- _contacts->list.adjustCurrent(yFrom, _rowHeight);
-
- DialogRow *drawFrom = _contacts->list.current;
- p.translate(0, drawFrom->pos * _rowHeight);
- while (drawFrom != _contacts->list.end && drawFrom->pos * _rowHeight < yTo) {
- paintDialog(p, drawFrom->history->peer, contactData(drawFrom), (drawFrom == _sel));
+ if (!_contacts->isEmpty()) {
+ auto i = _contacts->cfind(yFrom, _rowHeight);
+ p.translate(0, (*i)->pos() * _rowHeight);
+ for (auto end = _contacts->cend(); i != end; ++i) {
+ if ((*i)->pos() * _rowHeight >= yTo) {
+ break;
+ }
+ paintDialog(p, (*i)->history()->peer, contactData(*i), (*i == _sel));
p.translate(0, _rowHeight);
- drawFrom = drawFrom->next;
}
- yFrom -= _contacts->list.count * _rowHeight;
- yTo -= _contacts->list.count * _rowHeight;
+ yFrom -= _contacts->size() * _rowHeight;
+ yTo -= _contacts->size() * _rowHeight;
}
if (!_byUsername.isEmpty()) {
p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b);
@@ -605,7 +547,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
int32 to = ceilclamp(yTo, _rowHeight, 0, _filtered.size());
p.translate(0, from * _rowHeight);
for (; from < to; ++from) {
- paintDialog(p, _filtered[from]->history->peer, contactData(_filtered[from]), (_filteredSel == from));
+ paintDialog(p, _filtered[from]->history()->peer, contactData(_filtered[from]), (_filteredSel == from));
p.translate(0, _rowHeight);
}
}
@@ -640,10 +582,10 @@ void ContactsInner::updateSelectedRow() {
update(0, 0, width(), st::contactsNewItemHeight);
}
if (_sel) {
- update(0, _newItemHeight + _sel->pos * _rowHeight, width(), _rowHeight);
+ update(0, _newItemHeight + _sel->pos() * _rowHeight, width(), _rowHeight);
}
if (_byUsernameSel >= 0) {
- update(0, _newItemHeight + _contacts->list.count * _rowHeight + st::searchedBarHeight + _byUsernameSel * _rowHeight, width(), _rowHeight);
+ update(0, _newItemHeight + _contacts->size() * _rowHeight + st::searchedBarHeight + _byUsernameSel * _rowHeight, width(), _rowHeight);
}
} else {
if (_filteredSel >= 0) {
@@ -734,14 +676,14 @@ void ContactsInner::chooseParticipant() {
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) {
peer = _byUsername[_byUsernameSel];
} else if (_sel) {
- peer = _sel->history->peer;
+ peer = _sel->history()->peer;
}
} else {
if (_byUsernameSel >= 0 && _byUsernameSel < _byUsernameFiltered.size()) {
peer = _byUsernameFiltered[_byUsernameSel];
} else {
if (_filteredSel < 0 || _filteredSel >= _filtered.size()) return;
- peer = _filtered[_filteredSel]->history->peer;
+ peer = _filtered[_filteredSel]->history()->peer;
}
}
if (peer) {
@@ -771,8 +713,8 @@ void ContactsInner::chooseParticipant() {
update();
}
-void ContactsInner::changeCheckState(DialogRow *row) {
- changeCheckState(contactData(row), row->history->peer);
+void ContactsInner::changeCheckState(Dialogs::Row *row) {
+ changeCheckState(contactData(row), row->history()->peer);
}
void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) {
@@ -818,8 +760,8 @@ void ContactsInner::updateSel() {
}
p.setY(p.y() - _newItemHeight);
}
- DialogRow *newSel = (in && !newItemSel && (p.y() >= 0) && (p.y() < _contacts->list.count * _rowHeight)) ? _contacts->list.rowAtY(p.y(), _rowHeight) : 0;
- int32 byUsernameSel = (in && !newItemSel && p.y() >= _contacts->list.count * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->list.count * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1;
+ Dialogs::Row *newSel = (in && !newItemSel && (p.y() >= 0) && (p.y() < _contacts->size() * _rowHeight)) ? _contacts->rowAtY(p.y(), _rowHeight) : nullptr;
+ int32 byUsernameSel = (in && !newItemSel && p.y() >= _contacts->size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1;
if (byUsernameSel >= _byUsername.size()) byUsernameSel = -1;
if (newSel != _sel || byUsernameSel != _byUsernameSel || newItemSel != _newItemSel) {
updateSelectedRow();
@@ -879,23 +821,23 @@ void ContactsInner::updateFilter(QString filter) {
_filtered.clear();
if (!f.isEmpty()) {
- DialogsList *dialogsToFilter = 0;
- if (_contacts->list.count) {
+ const Dialogs::List *toFilter = nullptr;
+ if (!_contacts->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
- DialogsIndexed::DialogsIndex::iterator i = _contacts->index.find(fi->at(0));
- if (i == _contacts->index.cend()) {
- dialogsToFilter = 0;
+ auto found = _contacts->filtered(fi->at(0));
+ if (found->isEmpty()) {
+ toFilter = nullptr;
break;
}
- if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) {
- dialogsToFilter = i.value();
+ if (!toFilter || toFilter->size() > found->size()) {
+ toFilter = found;
}
}
}
- if (dialogsToFilter && dialogsToFilter->count) {
- _filtered.reserve(dialogsToFilter->count);
- for (DialogRow *i = dialogsToFilter->begin, *e = dialogsToFilter->end; i != e; i = i->next) {
- const PeerData::Names &names(i->history->peer->names);
+ if (toFilter) {
+ _filtered.reserve(toFilter->size());
+ for_const (auto row, *toFilter) {
+ const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
@@ -909,8 +851,8 @@ void ContactsInner::updateFilter(QString filter) {
}
}
if (fi == fe) {
- i->attached = 0;
- _filtered.push_back(i);
+ row->attached = nullptr;
+ _filtered.push_back(row);
}
}
}
@@ -964,7 +906,7 @@ void ContactsInner::updateFilter(QString filter) {
}
}
-void ContactsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
+void ContactsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) {
if (!_filter.isEmpty()) {
for (FilteredDialogs::iterator i = _filtered.begin(), e = _filtered.end(); i != e;) {
if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts!
@@ -987,8 +929,8 @@ void ContactsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
}
}
_mouseSel = false;
- int32 cnt = (_filter.isEmpty() ? _contacts->list.count : _filtered.size());
- int32 newh = cnt ? (cnt * _rowHeight) : st::noContactsHeight;
+ int cnt = (_filter.isEmpty() ? _contacts->size() : _filtered.size());
+ int newh = cnt ? (cnt * _rowHeight) : st::noContactsHeight;
resize(width(), newh);
}
@@ -1046,9 +988,9 @@ void ContactsInner::refresh() {
} else {
if (!_allAdmins.isHidden()) _allAdmins.hide();
}
- if (_contacts->list.count || !_byUsername.isEmpty()) {
+ if (!_contacts->isEmpty() || !_byUsername.isEmpty()) {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
- resize(width(), _newItemHeight + (_contacts->list.count * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight)));
+ resize(width(), _newItemHeight + (_contacts->size() * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight)));
} else if (_chat && _membersFilter == MembersFilterAdmins) {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
resize(width(), _newItemHeight + st::noContactsHeight);
@@ -1097,7 +1039,6 @@ ContactsInner::~ContactsInner() {
delete *i;
}
if (_bot || (_chat && _membersFilter == MembersFilterAdmins)) {
- delete _contacts;
if (_bot && _bot->botInfo) _bot->botInfo->startGroupToken = QString();
}
}
@@ -1113,12 +1054,12 @@ void ContactsInner::selectSkip(int32 dir) {
if (_filter.isEmpty()) {
int cur = 0;
if (_sel) {
- for (DialogRow *i = _contacts->list.begin; i != _sel; i = i->next) {
+ for (auto i = _contacts->cbegin(); *i != _sel; ++i) {
++cur;
}
if (_newItemHeight) ++cur;
} else if (_byUsernameSel >= 0) {
- cur = (_contacts->list.count + _byUsernameSel);
+ cur = (_contacts->size() + _byUsernameSel);
if (_newItemHeight) ++cur;
} else if (!_newItemSel) {
cur = -1;
@@ -1126,32 +1067,39 @@ void ContactsInner::selectSkip(int32 dir) {
cur += dir;
if (cur <= 0) {
_newItemSel = (_chat && _membersFilter == MembersFilterAdmins) ? false : (_newItemHeight ? true : false);
- _sel = (!_newItemHeight && _contacts->list.count) ? _contacts->list.begin : 0;
- _byUsernameSel = (!_newItemHeight && !_contacts->list.count && !_byUsername.isEmpty()) ? 0 : -1;
- } else if (cur >= _contacts->list.count + (_newItemHeight ? 1 : 0)) {
+ _sel = (!_newItemHeight && !_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr;
+ _byUsernameSel = (!_newItemHeight && _contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1;
+ } else if (cur >= _contacts->size() + (_newItemHeight ? 1 : 0)) {
_newItemSel = false;
if (_byUsername.isEmpty()) {
- _sel = _contacts->list.count ? _contacts->list.end->prev : 0;
+ _sel = _contacts->isEmpty() ? nullptr : *(_contacts->cend() - 1);
_byUsernameSel = -1;
} else {
- _sel = 0;
- _byUsernameSel = cur - _contacts->list.count;
+ _sel = nullptr;
+ _byUsernameSel = cur - _contacts->size();
if (_byUsernameSel >= _byUsername.size()) _byUsernameSel = _byUsername.size() - 1;
}
} else {
_newItemSel = false;
if (_newItemHeight) --cur;
- for (_sel = _contacts->list.begin; cur; _sel = _sel->next) {
- --cur;
+ for (auto i = _contacts->cbegin(); ; ++i) {
+ _sel = *i;
+ if (!cur) {
+ break;
+ } else {
+ --cur;
+ }
}
_byUsernameSel = -1;
}
if (dir > 0) {
- while (_sel && _sel->next && contactData(_sel)->inchat) {
- _sel = _sel->next;
+ for (auto i = _contacts->cfind(_sel), end = _contacts->cend(); i != end && contactData(*i)->inchat; ++i) {
+ _sel = *i;
}
- if (!_sel || !_sel->next) {
- _sel = 0;
+ if (_sel && contactData(_sel)->inchat) {
+ _sel = nullptr;
+ }
+ if (!_sel) {
if (!_byUsername.isEmpty()) {
if (_byUsernameSel < 0) _byUsernameSel = 0;
for (; _byUsernameSel < _byUsername.size() && d_byUsername[_byUsernameSel]->inchat;) {
@@ -1165,10 +1113,15 @@ void ContactsInner::selectSkip(int32 dir) {
--_byUsernameSel;
}
if (_byUsernameSel < 0) {
- if (_contacts->list.count) {
- if (!_newItemSel && !_sel) _sel = _contacts->list.end->prev;
- for (; _sel && contactData(_sel)->inchat;) {
- _sel = _sel->prev;
+ if (!_contacts->isEmpty()) {
+ if (!_newItemSel && !_sel) _sel = *(_contacts->cend() - 1);
+ if (_sel) {
+ for (auto i = _contacts->cfind(_sel), b = _contacts->cbegin(); i != b && contactData(*i)->inchat; --i) {
+ _sel = *i;
+ }
+ if (contactData(_sel)->inchat) {
+ _sel = nullptr;
+ }
}
}
}
@@ -1176,9 +1129,9 @@ void ContactsInner::selectSkip(int32 dir) {
if (_newItemSel) {
emit mustScrollTo(0, _newItemHeight);
} else if (_sel) {
- emit mustScrollTo(_newItemHeight + _sel->pos * _rowHeight, _newItemHeight + (_sel->pos + 1) * _rowHeight);
+ emit mustScrollTo(_newItemHeight + _sel->pos() * _rowHeight, _newItemHeight + (_sel->pos() + 1) * _rowHeight);
} else if (_byUsernameSel >= 0) {
- emit mustScrollTo(_newItemHeight + (_contacts->list.count + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _newItemHeight + (_contacts->list.count + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight);
+ emit mustScrollTo(_newItemHeight + (_contacts->size() + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _newItemHeight + (_contacts->size() + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight);
}
} else {
int cur = (_filteredSel >= 0) ? _filteredSel : ((_byUsernameSel >= 0) ? (_filtered.size() + _byUsernameSel) : -1);
@@ -1239,8 +1192,8 @@ void ContactsInner::selectSkipPage(int32 h, int32 dir) {
QVector ContactsInner::selected() {
QVector result;
- for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
- if (_checkedContacts.contains(row->history->peer)) {
+ for_const (auto row, *_contacts) {
+ if (_checkedContacts.contains(row->history()->peer)) {
contactData(row); // fill _contactsData
}
}
@@ -1260,8 +1213,8 @@ QVector ContactsInner::selected() {
QVector ContactsInner::selectedInputs() {
QVector result;
- for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
- if (_checkedContacts.contains(row->history->peer)) {
+ for_const (auto row, *_contacts) {
+ if (_checkedContacts.contains(row->history()->peer)) {
contactData(row); // fill _contactsData
}
}
@@ -1280,8 +1233,8 @@ QVector ContactsInner::selectedInputs() {
}
PeerData *ContactsInner::selectedUser() {
- for (DialogRow *row = _contacts->list.begin; row->next; row = row->next) {
- if (_checkedContacts.contains(row->history->peer)) {
+ for_const (auto row, *_contacts) {
+ if (_checkedContacts.contains(row->history()->peer)) {
contactData(row); // fill _contactsData
}
}
@@ -1474,7 +1427,7 @@ void ContactsBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId r
}
bool ContactsBox::peopleFailed(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (_peopleRequest == req) {
_peopleRequest = 0;
@@ -1619,7 +1572,7 @@ void ContactsBox::onCreate() {
if (_saveRequestId) return;
MTPVector users(MTP_vector(_inner.selectedInputs()));
- const QVector &v(users.c_vector().v);
+ const auto &v(users.c_vector().v);
if (v.isEmpty() || (v.size() == 1 && v.at(0).type() == mtpc_inputUserSelf)) {
_filter.setFocus();
_filter.showError();
@@ -1712,7 +1665,7 @@ void ContactsBox::removeAdminDone(UserData *user, const MTPBool &result) {
}
bool ContactsBox::saveAdminsFail(const RPCError &error) {
- if (mtpIsFlood(error)) return true;
+ if (MTP::isDefaultHandledError(error)) return true;
_saveRequestId = 0;
_inner.saving(false);
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
@@ -1722,7 +1675,7 @@ bool ContactsBox::saveAdminsFail(const RPCError &error) {
}
bool ContactsBox::editAdminFail(const RPCError &error) {
- if (mtpIsFlood(error)) return true;
+ if (MTP::isDefaultHandledError(error)) return true;
--_saveRequestId;
_inner.chat()->invalidateParticipants();
if (!_saveRequestId) {
@@ -1765,7 +1718,7 @@ void ContactsBox::creationDone(const MTPUpdates &updates) {
}
bool ContactsBox::creationFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_saveRequestId = 0;
if (error.type() == "NO_CHAT_TITLE") {
@@ -1776,7 +1729,7 @@ bool ContactsBox::creationFail(const RPCError &error) {
_filter.showError();
return true;
} else if (error.type() == "PEER_FLOOD") {
- Ui::showLayer(new InformBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))), KeepOtherLayers);
+ Ui::showLayer(new InformBox(cantInviteError()), KeepOtherLayers);
return true;
} else if (error.type() == qstr("USER_RESTRICTED")) {
Ui::showLayer(new InformBox(lang(lng_cant_do_this)));
@@ -1807,7 +1760,7 @@ MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : TWidget
, _about(_aboutWidth)
, _aboutHeight(0) {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
- connect(App::main(), SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)));
+ connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
refresh();
@@ -2157,8 +2110,8 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
_loadingRequestId = 0;
if (result.type() == mtpc_channels_channelParticipants) {
- const MTPDchannels_channelParticipants &d(result.c_channels_channelParticipants());
- const QVector &v(d.vparticipants.c_vector().v);
+ const auto &d(result.c_channels_channelParticipants());
+ const auto &v(d.vparticipants.c_vector().v);
_rows.reserve(v.size());
_datas.reserve(v.size());
_dates.reserve(v.size());
@@ -2238,7 +2191,8 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
}
bool MembersInner::membersFailed(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
+
Ui::hideLayer();
return true;
}
@@ -2259,7 +2213,7 @@ void MembersInner::kickAdminDone(const MTPUpdates &result, mtpRequestId req) {
}
bool MembersInner::kickFail(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (_kickBox) _kickBox->onClose();
load();
diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h
index 6f9eed12d..e1bb53673 100644
--- a/Telegram/SourceFiles/boxes/contactsbox.h
+++ b/Telegram/SourceFiles/boxes/contactsbox.h
@@ -22,12 +22,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "abstractbox.h"
+namespace Dialogs {
+class Row;
+class IndexedList;
+} // namespace Dialogs
+
enum MembersFilter {
MembersFilterRecent,
MembersFilterAdmins,
};
typedef QMap MembersAlreadyIn;
+QString cantInviteError();
+
class ConfirmBox;
class ContactsInner : public TWidget, public RPCSender {
Q_OBJECT
@@ -51,7 +58,7 @@ public:
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
-
+
void paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel);
void updateFilter(QString filter = QString());
@@ -67,7 +74,7 @@ public:
void loadProfilePhotos(int32 yFrom);
void chooseParticipant();
- void changeCheckState(DialogRow *row);
+ void changeCheckState(Dialogs::Row *row);
void changeCheckState(ContactData *data, PeerData *peer);
void peopleReceived(const QString &query, const QVector &people);
@@ -100,7 +107,7 @@ signals:
public slots:
- void onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow);
+ void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow);
void updateSel();
void peerUpdated(PeerData *peer);
@@ -118,36 +125,38 @@ private:
void addAdminDone(const MTPUpdates &result, mtpRequestId req);
bool addAdminFail(const RPCError &error, mtpRequestId req);
- int32 _rowHeight, _newItemHeight;
- bool _newItemSel;
+ int32 _rowHeight;
+ int _newItemHeight = 0;
+ bool _newItemSel = false;
- ChatData *_chat;
- ChannelData *_channel;
- MembersFilter _membersFilter;
- UserData *_bot;
- CreatingGroupType _creating;
+ ChatData *_chat = nullptr;
+ ChannelData *_channel = nullptr;
+ MembersFilter _membersFilter = MembersFilterRecent;
+ UserData *_bot = nullptr;
+ CreatingGroupType _creating = CreatingGroupNone;
MembersAlreadyIn _already;
Checkbox _allAdmins;
int32 _aboutWidth;
Text _aboutAllAdmins, _aboutAdmins;
- PeerData *_addToPeer;
- UserData *_addAdmin;
- mtpRequestId _addAdminRequestId;
- ConfirmBox *_addAdminBox;
-
+ PeerData *_addToPeer = nullptr;
+ UserData *_addAdmin = nullptr;
+ mtpRequestId _addAdminRequestId = 0;
+ ConfirmBox *_addAdminBox = nullptr;
+
int32 _time;
- DialogsIndexed *_contacts;
- DialogRow *_sel;
+ std_::unique_ptr _customList;
+ Dialogs::IndexedList *_contacts = nullptr;
+ Dialogs::Row *_sel = nullptr;
QString _filter;
- typedef QVector FilteredDialogs;
+ typedef QVector FilteredDialogs;
FilteredDialogs _filtered;
- int32 _filteredSel;
- bool _mouseSel;
+ int _filteredSel = -1;
+ bool _mouseSel = false;
- int32 _selCount;
+ int _selCount = 0;
struct ContactData {
Text name;
@@ -161,22 +170,22 @@ private:
typedef QMap CheckedContacts;
CheckedContacts _checkedContacts;
- ContactData *contactData(DialogRow *row);
+ ContactData *contactData(Dialogs::Row *row);
- bool _searching;
+ bool _searching = false;
QString _lastQuery;
typedef QVector ByUsernameRows;
typedef QVector ByUsernameDatas;
ByUsernameRows _byUsername, _byUsernameFiltered;
ByUsernameDatas d_byUsername, d_byUsernameFiltered; // filtered is partly subset of d_byUsername, partly subset of _byUsernameDatas
ByUsernameDatas _byUsernameDatas;
- int32 _byUsernameSel;
+ int _byUsernameSel = -1;
QPoint _lastMousePos;
LinkButton _addContactLnk;
- bool _saving;
- bool _allAdminsChecked;
+ bool _saving = false;
+ bool _allAdminsChecked = false;
};
diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp
index 00c7a7158..3da42b769 100644
--- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp
+++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp
@@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h"
#include "downloadpathbox.h"
-#include "gui/filedialog.h"
+#include "ui/filedialog.h"
#include "pspecific.h"
DownloadPathBox::DownloadPathBox() : AbstractBox()
diff --git a/Telegram/SourceFiles/boxes/emojibox.cpp b/Telegram/SourceFiles/boxes/emojibox.cpp
index 9bd637bed..9aca04bc0 100644
--- a/Telegram/SourceFiles/boxes/emojibox.cpp
+++ b/Telegram/SourceFiles/boxes/emojibox.cpp
@@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "emojibox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
namespace {
// copied from genemoji.cpp
diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp
index f6e7daf2f..8c7fe47ed 100644
--- a/Telegram/SourceFiles/boxes/languagebox.cpp
+++ b/Telegram/SourceFiles/boxes/languagebox.cpp
@@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "languagebox.h"
#include "confirmbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
#include "langloaderplain.h"
@@ -46,12 +46,12 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) {
for (int32 i = 0; i < languageCount; ++i) {
LangLoaderResult result;
if (i) {
- LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i] + qsl(".strings"), LangLoaderRequest(lng_language_name));
+ LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name));
result = loader.found();
} else {
result.insert(lng_language_name, langOriginal(lng_language_name));
}
- _langs.push_back(new Radiobutton(this, qsl("lang"), i, result.value(lng_language_name, LanguageCodes[i] + qsl(" language")), (cLang() == i), st::langsButton));
+ _langs.push_back(new Radiobutton(this, qsl("lang"), i, result.value(lng_language_name, LanguageCodes[i].c_str() + qsl(" language")), (cLang() == i), st::langsButton));
_langs.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
y += _langs.back()->height() + st::boxOptionListPadding.top();
connect(_langs.back(), SIGNAL(changed()), this, SLOT(onChange()));
@@ -82,14 +82,14 @@ void LanguageBox::showAll() {
void LanguageBox::mousePressEvent(QMouseEvent *e) {
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
for (int32 i = 1; i < languageCount; ++i) {
- LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i] + qsl(".strings"), LangLoaderRequest(lngkeys_cnt));
+ LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lngkeys_cnt));
if (!loader.errors().isEmpty()) {
- Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i] + qsl("\" error :(\n\nError: ") + loader.errors()));
+ Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors()));
return;
} else if (!loader.warnings().isEmpty()) {
QString warn = loader.warnings();
if (warn.size() > 256) warn = warn.mid(0, 253) + qsl("...");
- Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i] + qsl("\" warnings :(\n\nWarnings: ") + warn));
+ Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" warnings :(\n\nWarnings: ") + warn));
return;
}
}
@@ -112,7 +112,7 @@ void LanguageBox::onChange() {
if (_langs[i]->checked() && langId != cLang()) {
LangLoaderResult result;
if (langId > 0) {
- LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId] + qsl(".strings"), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
+ LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
result = loader.found();
} else if (langId == languageTest) {
LangLoaderPlain loader(cLangFile(), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp
index c90e4c0b4..8468e86ff 100644
--- a/Telegram/SourceFiles/boxes/passcodebox.cpp
+++ b/Telegram/SourceFiles/boxes/passcodebox.cpp
@@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "passcodebox.h"
#include "confirmbox.h"
-#include "window.h"
+#include "mainwindow.h"
#include "localstorage.h"
@@ -284,35 +284,12 @@ void PasscodeBox::setPasswordDone(const MTPBool &result) {
}
bool PasscodeBox::setPasswordFail(const RPCError &error) {
- if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
- _setRequest = 0;
- QString err = error.type();
- if (err == "PASSWORD_HASH_INVALID") {
- if (_oldPasscode.isHidden()) {
- emit reloadPassword();
- onClose();
- } else {
- onBadOldPasscode();
- }
- } else if (err == "NEW_PASSWORD_BAD") {
- _newPasscode.setFocus();
- _newPasscode.showError();
- _newError = lang(lng_cloud_password_bad);
- update();
- } else if (err == "NEW_SALT_INVALID") {
- emit reloadPassword();
- onClose();
- } else if (err == "EMAIL_INVALID") {
- _emailError = lang(lng_cloud_password_bad_email);
- _recoverEmail.setFocus();
- _recoverEmail.showError();
- update();
- } else if (err == "EMAIL_UNCONFIRMED") {
- Ui::showLayer(new InformBox(lang(lng_cloud_password_almost)));
- emit reloadPassword();
- } else if (mtpIsFlood(error)) {
+ if (MTP::isFloodError(error)) {
if (_oldPasscode.isHidden()) return false;
+ if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
+ _setRequest = 0;
+
_oldPasscode.selectAll();
_oldPasscode.setFocus();
_oldPasscode.showError();
@@ -321,6 +298,36 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
_recover.hide();
}
update();
+ return true;
+ }
+ if (MTP::isDefaultHandledError(error)) return false;
+
+ if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose();
+ _setRequest = 0;
+ QString err = error.type();
+ if (err == qstr("PASSWORD_HASH_INVALID")) {
+ if (_oldPasscode.isHidden()) {
+ emit reloadPassword();
+ onClose();
+ } else {
+ onBadOldPasscode();
+ }
+ } else if (err == qstr("NEW_PASSWORD_BAD")) {
+ _newPasscode.setFocus();
+ _newPasscode.showError();
+ _newError = lang(lng_cloud_password_bad);
+ update();
+ } else if (err == qstr("NEW_SALT_INVALID")) {
+ emit reloadPassword();
+ onClose();
+ } else if (err == qstr("EMAIL_INVALID")) {
+ _emailError = lang(lng_cloud_password_bad_email);
+ _recoverEmail.setFocus();
+ _recoverEmail.showError();
+ update();
+ } else if (err == qstr("EMAIL_UNCONFIRMED")) {
+ Ui::showLayer(new InformBox(lang(lng_cloud_password_almost)));
+ emit reloadPassword();
}
return true;
}
@@ -404,8 +411,8 @@ void PasscodeBox::onSave(bool force) {
if (_oldPasscode.isHidden() || _newPasscode.isHidden()) {
flags |= MTPDaccount_passwordInputSettings::Flag::f_email;
}
- MTPaccount_PasswordInputSettings settings(MTP_account_passwordInputSettings(MTP_flags(flags), MTP_string(_newSalt), MTP_string(newPasswordHash), MTP_string(hint), MTP_string(email)));
- _setRequest = MTP::send(MTPaccount_UpdatePasswordSettings(MTP_string(oldPasswordHash), settings), rpcDone(&PasscodeBox::setPasswordDone), rpcFail(&PasscodeBox::setPasswordFail));
+ MTPaccount_PasswordInputSettings settings(MTP_account_passwordInputSettings(MTP_flags(flags), MTP_bytes(_newSalt), MTP_bytes(newPasswordHash), MTP_string(hint), MTP_string(email)));
+ _setRequest = MTP::send(MTPaccount_UpdatePasswordSettings(MTP_bytes(oldPasswordHash), settings), rpcDone(&PasscodeBox::setPasswordDone), rpcFail(&PasscodeBox::setPasswordFail));
}
} else {
cSetPasscodeBadTries(0);
@@ -490,7 +497,7 @@ void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
}
bool PasscodeBox::recoverStartFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_pattern = QString();
onClose();
@@ -587,32 +594,36 @@ void RecoverBox::codeSubmitDone(bool recover, const MTPauth_Authorization &resul
}
bool RecoverBox::codeSubmitFail(const RPCError &error) {
+ if (MTP::isFloodError(error)) {
+ _submitRequest = 0;
+ _error = lang(lng_flood_error);
+ update();
+ _recoverCode.showError();
+ return true;
+ }
+ if (MTP::isDefaultHandledError(error)) return false;
+
_submitRequest = 0;
const QString &err = error.type();
- if (err == "PASSWORD_EMPTY") {
+ if (err == qstr("PASSWORD_EMPTY")) {
emit reloadPassword();
Ui::showLayer(new InformBox(lang(lng_cloud_password_removed)));
return true;
- } else if (err == "PASSWORD_RECOVERY_NA") {
+ } else if (err == qstr("PASSWORD_RECOVERY_NA")) {
onClose();
return true;
- } else if (err == "PASSWORD_RECOVERY_EXPIRED") {
+ } else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
emit recoveryExpired();
onClose();
return true;
- } else if (err == "CODE_INVALID") {
+ } else if (err == qstr("CODE_INVALID")) {
_error = lang(lng_signin_wrong_code);
update();
_recoverCode.selectAll();
_recoverCode.setFocus();
_recoverCode.showError();
return true;
- } else if (mtpIsFlood(error)) {
- _error = lang(lng_flood_error);
- update();
- _recoverCode.showError();
- return true;
}
if (cDebug()) { // internal server error
_error = err + ": " + error.description();
diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp
index d7de86d98..0a2d477a3 100644
--- a/Telegram/SourceFiles/boxes/photocropbox.cpp
+++ b/Telegram/SourceFiles/boxes/photocropbox.cpp
@@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
-#include "style.h"
+#include "ui/style.h"
#include "lang.h"
#include "application.h"
diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp
index f3a202ccc..7667d3471 100644
--- a/Telegram/SourceFiles/boxes/photosendbox.cpp
+++ b/Telegram/SourceFiles/boxes/photosendbox.cpp
@@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
-#include "style.h"
+#include "ui/style.h"
#include "lang.h"
#include "localstorage.h"
@@ -51,8 +51,8 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW
if (_file->photo.type() != mtpc_photoEmpty) {
_file->type = PreparePhoto;
} else if (_file->document.type() == mtpc_document) {
- const MTPDdocument &document(_file->document.c_document());
- const QVector &attributes(document.vattributes.c_vector().v);
+ const auto &document(_file->document.c_document());
+ const auto &attributes(document.vattributes.c_vector().v);
for (int32 i = 0, l = attributes.size(); i < l; ++i) {
if (attributes.at(i).type() == mtpc_documentAttributeAnimated) {
_animated = true;
@@ -648,15 +648,15 @@ void EditCaptionBox::onSave(bool ctrlShiftEnter) {
return;
}
- MTPchannels_EditMessage::Flags flags = 0;
+ MTPmessages_EditMessage::Flags flags = MTPmessages_EditMessage::Flag::f_message;
if (_previewCancelled) {
- flags |= MTPchannels_EditMessage::Flag::f_no_webpage;
+ flags |= MTPmessages_EditMessage::Flag::f_no_webpage;
}
MTPVector sentEntities;
if (!sentEntities.c_vector().v.isEmpty()) {
- flags |= MTPchannels_EditMessage::Flag::f_entities;
+ flags |= MTPmessages_EditMessage::Flag::f_entities;
}
- _saveRequestId = MTP::send(MTPchannels_EditMessage(MTP_flags(flags), item->history()->peer->asChannel()->inputChannel, MTP_int(item->id), MTP_string(_field->getLastText()), sentEntities), rpcDone(&EditCaptionBox::saveDone), rpcFail(&EditCaptionBox::saveFail));
+ _saveRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(flags), item->history()->peer->input, MTP_int(item->id), MTP_string(_field->getLastText()), MTPnullMarkup, sentEntities), rpcDone(&EditCaptionBox::saveDone), rpcFail(&EditCaptionBox::saveFail));
}
void EditCaptionBox::saveDone(const MTPUpdates &updates) {
@@ -668,7 +668,7 @@ void EditCaptionBox::saveDone(const MTPUpdates &updates) {
}
bool EditCaptionBox::saveFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_saveRequestId = 0;
QString err = error.type();
diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp
index be56624fa..06d84ced1 100644
--- a/Telegram/SourceFiles/boxes/sessionsbox.cpp
+++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp
@@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "sessionsbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
#include "countries.h"
#include "confirmbox.h"
@@ -166,7 +166,7 @@ void SessionsInner::terminateDone(uint64 hash, const MTPBool &result) {
}
bool SessionsInner::terminateFail(uint64 hash, const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
TerminateButtons::iterator i = _terminateButtons.find(hash);
if (i != _terminateButtons.end()) {
@@ -181,7 +181,7 @@ void SessionsInner::terminateAllDone(const MTPBool &result) {
}
bool SessionsInner::terminateAllFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
emit allTerminated();
return true;
}
@@ -294,14 +294,14 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
int32 availOther = availCurrent - st::sessionTerminate.iconPos.x();// -st::sessionTerminate.width - st::sessionTerminateSkip;
_list.clear();
- const QVector &v(result.c_account_authorizations().vauthorizations.c_vector().v);
+ const auto &v(result.c_account_authorizations().vauthorizations.c_vector().v);
int32 l = v.size();
if (l > 1) _list.reserve(l - 1);
const CountriesByISO2 &countries(countriesByISO2());
for (int32 i = 0; i < l; ++i) {
- const MTPDauthorization &d(v.at(i).c_authorization());
+ const auto &d(v.at(i).c_authorization());
SessionData data;
data.hash = d.vhash.v;
diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp
index fd0d70e0a..814958521 100644
--- a/Telegram/SourceFiles/boxes/stickersetbox.cpp
+++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp
@@ -23,10 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stickersetbox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
#include "settingswidget.h"
#include "boxes/confirmbox.h"
-
+#include "apiwrap.h"
#include "localstorage.h"
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget()
@@ -56,8 +56,8 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.clear();
_emoji.clear();
if (set.type() == mtpc_messages_stickerSet) {
- const MTPDmessages_stickerSet &d(set.c_messages_stickerSet());
- const QVector &v(d.vdocuments.c_vector().v);
+ const auto &d(set.c_messages_stickerSet());
+ const auto &v(d.vdocuments.c_vector().v);
_pack.reserve(v.size());
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
@@ -65,12 +65,12 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.push_back(doc);
}
- const QVector &packs(d.vpacks.c_vector().v);
+ const auto &packs(d.vpacks.c_vector().v);
for (int32 i = 0, l = packs.size(); i < l; ++i) {
if (packs.at(i).type() != mtpc_stickerPack) continue;
- const MTPDstickerPack &pack(packs.at(i).c_stickerPack());
+ const auto &pack(packs.at(i).c_stickerPack());
if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
- const QVector &stickers(pack.vdocuments.c_vector().v);
+ const auto &stickers(pack.vdocuments.c_vector().v);
StickerPack p;
p.reserve(stickers.size());
for (int32 j = 0, c = stickers.size(); j < c; ++j) {
@@ -83,7 +83,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
}
}
if (d.vset.type() == mtpc_stickerSet) {
- const MTPDstickerSet &s(d.vset.c_stickerSet());
+ const auto &s(d.vset.c_stickerSet());
_setTitle = stickerSetTitle(s);
_title = st::boxTitleFont->elided(_setTitle, width() - st::boxTitlePosition.x() - st::boxTitleHeight);
_setShortName = qs(s.vshort_name);
@@ -107,7 +107,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
}
bool StickerSetInner::failedSet(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_loaded = true;
@@ -152,7 +152,7 @@ void StickerSetInner::installDone(const MTPBool &result) {
}
bool StickerSetInner::installFailed(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
Ui::showLayer(new InformBox(lang(lng_stickers_not_found)));
@@ -171,7 +171,7 @@ void StickerSetInner::mouseMoveEvent(QMouseEvent *e) {
int32 index = stickerFromGlobalPos(e->globalPos());
if (index >= 0 && index < _pack.size() && index != _previewShown) {
_previewShown = index;
- Ui::showStickerPreview(_pack.at(_previewShown));
+ Ui::showMediaPreview(_pack.at(_previewShown));
}
}
}
@@ -184,7 +184,7 @@ void StickerSetInner::onPreview() {
int32 index = stickerFromGlobalPos(QCursor::pos());
if (index >= 0 && index < _pack.size()) {
_previewShown = index;
- Ui::showStickerPreview(_pack.at(_previewShown));
+ Ui::showMediaPreview(_pack.at(_previewShown));
}
}
@@ -803,7 +803,7 @@ void StickersBox::disenableDone(const MTPBool & result, mtpRequestId req) {
}
bool StickersBox::disenableFail(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_disenableRequests.remove(req);
if (_disenableRequests.isEmpty()) {
saveOrder();
@@ -831,7 +831,7 @@ void StickersBox::reorderDone(const MTPBool &result) {
}
bool StickersBox::reorderFail(const RPCError &result) {
- if (mtpIsFlood(result)) return false;
+ if (MTP::isDefaultHandledError(result)) return false;
_reorderRequest = 0;
Global::SetLastStickersUpdate(0);
App::main()->updateStickers();
diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp
index 247db630f..7d7bb157f 100644
--- a/Telegram/SourceFiles/boxes/usernamebox.cpp
+++ b/Telegram/SourceFiles/boxes/usernamebox.cpp
@@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "usernamebox.h"
#include "mainwidget.h"
-#include "window.h"
+#include "mainwindow.h"
UsernameBox::UsernameBox() : AbstractBox(st::boxWidth),
_save(this, lang(lng_settings_save), st::defaultBoxButton),
@@ -202,22 +202,22 @@ void UsernameBox::onUpdateDone(const MTPUser &user) {
}
bool UsernameBox::onUpdateFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_saveRequestId = 0;
QString err(error.type());
- if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == App::self()->username) {
+ if (err == qstr("USERNAME_NOT_MODIFIED") || _sentUsername == App::self()->username) {
App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(_sentUsername));
emit closed();
return true;
- } else if (err == "USERNAME_INVALID") {
+ } else if (err == qstr("USERNAME_INVALID")) {
_username.setFocus();
_username.showError();
_copiedTextLink = QString();
_errorText = lang(lng_username_invalid);
update();
return true;
- } else if (err == "USERNAME_OCCUPIED" || err == "USERNAMES_UNAVAILABLE") {
+ } else if (err == qstr("USERNAME_OCCUPIED") || err == qstr("USERNAMES_UNAVAILABLE")) {
_username.setFocus();
_username.showError();
_copiedTextLink = QString();
@@ -242,15 +242,15 @@ void UsernameBox::onCheckDone(const MTPBool &result) {
}
bool UsernameBox::onCheckFail(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
_checkRequestId = 0;
QString err(error.type());
- if (err == "USERNAME_INVALID") {
+ if (err == qstr("USERNAME_INVALID")) {
_errorText = lang(lng_username_invalid);
update();
return true;
- } else if (err == "USERNAME_OCCUPIED" && _checkUsername != App::self()->username) {
+ } else if (err == qstr("USERNAME_OCCUPIED") && _checkUsername != App::self()->username) {
_errorText = lang(lng_username_occupied);
update();
return true;
diff --git a/Telegram/SourceFiles/codegen/style/main.cpp b/Telegram/SourceFiles/codegen/style/main.cpp
new file mode 100644
index 000000000..a553b5173
--- /dev/null
+++ b/Telegram/SourceFiles/codegen/style/main.cpp
@@ -0,0 +1,3 @@
+int main(int argc, char *argv[]) {
+ return 0;
+}
diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h
index 39e5008f3..948dd8380 100644
--- a/Telegram/SourceFiles/config.h
+++ b/Telegram/SourceFiles/config.h
@@ -20,10 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
-static const int32 AppVersion = 9036;
-static const wchar_t *AppVersionStr = L"0.9.36";
-static const bool DevVersion = true;
-//#define BETA_VERSION (9034004ULL) // just comment this line to build public version
+static const int32 AppVersion = 9042;
+static const wchar_t *AppVersionStr = L"0.9.42";
+static const bool DevVersion = false;
+//#define BETA_VERSION (9040128ULL) // just comment this line to build public version
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";
@@ -139,7 +139,6 @@ enum {
EmojiPanRowsPerPage = 6,
StickerPanPerRow = 5,
StickerPanRowsPerPage = 4,
- SavedGifsMaxPerRow = 4,
StickersUpdateTimeout = 3600000, // update not more than once in an hour
SearchPeopleLimit = 5,
@@ -296,7 +295,7 @@ static const char *ApiHash = "344583e45741c457fe1862106095a5eb";
#else
static const char *BetaPrivateKey = "";
#undef BETA_VERSION
-#define BETA_VERSION 0
+#define BETA_VERSION (0)
#endif
inline const char *cApiDeviceModel() {
diff --git a/Telegram/SourceFiles/types.cpp b/Telegram/SourceFiles/core/basic_types.cpp
similarity index 99%
rename from Telegram/SourceFiles/types.cpp
rename to Telegram/SourceFiles/core/basic_types.cpp
index e4541be90..5d1bc6874 100644
--- a/Telegram/SourceFiles/types.cpp
+++ b/Telegram/SourceFiles/core/basic_types.cpp
@@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
-#include "types.h"
+#include "basic_types.h"
#include
#include
diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/core/basic_types.h
similarity index 79%
rename from Telegram/SourceFiles/types.h
rename to Telegram/SourceFiles/core/basic_types.h
index 836b7af37..75757ba02 100644
--- a/Telegram/SourceFiles/types.h
+++ b/Telegram/SourceFiles/core/basic_types.h
@@ -169,12 +169,8 @@ template char(&ArraySizeHelper(T(&array)[N]))[N];
// 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
// if you have "QVector v" then "for (T * const p : v)" will still call QVector::detach(),
-// while "for_const(T *p, v)" won't and "for_const(T *&p, v)" won't compile
-template
-struct ForConstTraits {
- typedef const T &ExpressionType;
-};
-#define for_const(range_declaration, range_expression) for (range_declaration : static_cast::ExpressionType>(range_expression))
+// while "for_const (T *p, v)" won't and "for_const (T *&p, v)" won't compile
+#define for_const(range_declaration, range_expression) for (range_declaration : std_::as_const(range_expression))
template
inline QFlags qFlags(Enum v) {
@@ -198,6 +194,31 @@ T *SharedMemoryLocation() {
return reinterpret_cast(_SharedMemoryLocation + N);
}
+// see https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf
+class str_const { // constexpr string
+public:
+ template
+ constexpr str_const(const char(&a)[N]) : _str(a), _size(N - 1) {
+ }
+ constexpr char operator[](std::size_t n) const {
+ return (n < _size) ? _str[n] :
+ throw std::out_of_range("");
+ }
+ constexpr std::size_t size() const { return _size; }
+ const char *c_str() const { return _str; }
+
+private:
+ const char* const _str;
+ const std::size_t _size;
+
+};
+
+template
+inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; }
+
+template
+inline void accumulate_min(T &a, const T &b) { if (a > b) a = b; }
+
#ifdef Q_OS_WIN
typedef float float32;
typedef double float64;
@@ -215,40 +236,187 @@ typedef double float64;
using std::string;
using std::exception;
-using std::swap;
-// we copy some parts of C++11 std:: library, because on OS X 10.6+
-// version we can use C++11, but we can't use its library :(
-namespace std11 {
+// we copy some parts of C++11/14/17 std:: library, because on OS X 10.6+
+// version we can use C++11/14/17, but we can not use its library :(
+namespace std_ {
+
+template
+struct integral_constant {
+ static constexpr T value = V;
+
+ using value_type = T;
+ using type = integral_constant;
+
+ constexpr operator value_type() const noexcept {
+ return (value);
+ }
+
+ constexpr value_type operator()() const noexcept {
+ return (value);
+ }
+};
+
+using true_type = integral_constant;
+using false_type = integral_constant;
template
struct remove_reference {
- typedef T type;
+ using type = T;
};
template
struct remove_reference {
- typedef T type;
+ using type = T;
};
template
struct remove_reference {
- typedef T type;
+ using type = T;
};
template
-inline typename remove_reference::type &&move(T &&value) {
+struct is_lvalue_reference : false_type {
+};
+template
+struct is_lvalue_reference : true_type {
+};
+
+template
+struct is_rvalue_reference : false_type {
+};
+template
+struct is_rvalue_reference : true_type {
+};
+
+template
+inline constexpr T &&forward(typename remove_reference::type &value) noexcept {
+ return static_cast(value);
+}
+template
+inline constexpr T &&forward(typename remove_reference::type &&value) noexcept {
+ static_assert(!is_lvalue_reference::value, "bad forward call");
+ return static_cast(value);
+}
+
+template
+inline constexpr typename remove_reference::type &&move(T &&value) noexcept {
return static_cast::type&&>(value);
}
-} // namespace std11
+template
+struct add_const {
+ using type = const T;
+};
+template
+using add_const_t = typename add_const::type;
+template
+constexpr add_const_t &as_const(T& t) noexcept {
+ return t;
+}
+template
+void as_const(const T&&) = delete;
+
+// This is not full unique_ptr, but at least with std interface.
+template
+class unique_ptr {
+public:
+ constexpr unique_ptr() noexcept = default;
+ unique_ptr(const unique_ptr &) = delete;
+ unique_ptr &operator=(const unique_ptr &) = delete;
+
+ constexpr unique_ptr(std::nullptr_t) {
+ }
+ unique_ptr &operator=(std::nullptr_t) noexcept {
+ reset();
+ return (*this);
+ }
+
+ explicit unique_ptr(T *p) noexcept : _p(p) {
+ }
+
+ template
+ unique_ptr(unique_ptr &&other) noexcept : _p(other.release()) {
+ }
+ template
+ unique_ptr &operator=(unique_ptr &&other) noexcept {
+ reset(other.release());
+ return (*this);
+ }
+ unique_ptr &operator=(unique_ptr &&other) noexcept {
+ if (this != &other) {
+ reset(other.release());
+ }
+ return (*this);
+ }
+
+ void swap(unique_ptr &other) noexcept {
+ std::swap(_p, other._p);
+ }
+ ~unique_ptr() noexcept {
+ delete _p;
+ }
+
+ T &operator*() const {
+ return (*get());
+ }
+ T *operator->() const noexcept {
+ return get();
+ }
+ T *get() const noexcept {
+ return _p;
+ }
+ explicit operator bool() const noexcept {
+ return get() != nullptr;
+ }
+
+ T *release() noexcept {
+ return getPointerAndReset(_p);
+ }
+
+ void reset(T *p = nullptr) noexcept {
+ T *old = _p;
+ _p = p;
+ if (old) {
+ delete old;
+ }
+ }
+
+private:
+ T *_p = nullptr;
+
+};
+
+template
+inline unique_ptr make_unique(Args&&... args) {
+ return unique_ptr(new T(forward(args)...));
+}
+
+template
+inline bool operator==(const unique_ptr &a, std::nullptr_t) noexcept {
+ return !a;
+}
+template
+inline bool operator==(std::nullptr_t, const unique_ptr &b) noexcept {
+ return !b;
+}
+template
+inline bool operator!=(const unique_ptr &a, std::nullptr_t b) noexcept {
+ return !(a == b);
+}
+template
+inline bool operator!=(std::nullptr_t a, const unique_ptr &b) noexcept {
+ return !(a == b);
+}
+
+} // namespace std_
#include "logs.h"
-static volatile int *t_assert_nullptr = 0;
+static volatile int *t_assert_nullptr = nullptr;
inline void t_noop() {}
inline void t_assert_fail(const char *message, const char *file, int32 line) {
QString info(qsl("%1 %2:%3").arg(message).arg(file).arg(line));
LOG(("Assertion Failed! %1 %2:%3").arg(info));
- SignalHandlers::setAssertionInfo(info);
+ SignalHandlers::setCrashAnnotation("Assertion", info);
*t_assert_nullptr = 0;
}
#define t_assert_full(condition, message, file, line) ((!(condition)) ? t_assert_fail(message, file, line) : t_noop())
@@ -595,20 +763,22 @@ MimeType mimeTypeForName(const QString &mime);
MimeType mimeTypeForFile(const QFileInfo &file);
MimeType mimeTypeForData(const QByteArray &data);
-inline int32 rowscount(int32 count, int32 perrow) {
- return (count + perrow - 1) / perrow;
+#include
+
+inline int rowscount(int fullCount, int countPerRow) {
+ return (fullCount + countPerRow - 1) / countPerRow;
}
-inline int32 floorclamp(int32 value, int32 step, int32 lowest, int32 highest) {
+inline int floorclamp(int value, int step, int lowest, int highest) {
return qMin(qMax(value / step, lowest), highest);
}
-inline int32 floorclamp(float64 value, int32 step, int32 lowest, int32 highest) {
- return qMin(qMax(qFloor(value / step), lowest), highest);
+inline int floorclamp(float64 value, int step, int lowest, int highest) {
+ return qMin(qMax(static_cast(std::floor(value / step)), lowest), highest);
}
-inline int32 ceilclamp(int32 value, int32 step, int32 lowest, int32 highest) {
- return qMax(qMin((value / step) + ((value % step) ? 1 : 0), highest), lowest);
+inline int ceilclamp(int value, int step, int lowest, int highest) {
+ return qMax(qMin((value + step - 1) / step, highest), lowest);
}
-inline int32 ceilclamp(float64 value, int32 step, int32 lowest, int32 highest) {
- return qMax(qMin(qCeil(value / step), highest), lowest);
+inline int ceilclamp(float64 value, int32 step, int32 lowest, int32 highest) {
+ return qMax(qMin(static_cast(std::ceil(value / step)), highest), lowest);
}
enum ForwardWhatMessages {
@@ -633,41 +803,110 @@ static int32 QuarterArcLength = (FullArcLength / 4);
static int32 MinArcLength = (FullArcLength / 360);
static int32 AlmostFullArcLength = (FullArcLength - MinArcLength);
-template
-class RefPairImplementation {
+template
+inline QSharedPointer MakeShared(Args&&... args) {
+ return QSharedPointer(new T(std_::forward(args)...));
+}
+
+// This pointer is used for global non-POD variables that are allocated
+// on demand by createIfNull(lambda) and are never automatically freed.
+template
+class NeverFreedPointer {
public:
- template
- const RefPairImplementation &operator=(const RefPairImplementation &other) const {
- _first = other._first;
- _second = other._second;
- return *this;
+ explicit NeverFreedPointer() {
+ }
+ NeverFreedPointer(const NeverFreedPointer &other) = delete;
+ NeverFreedPointer &operator=(const NeverFreedPointer &other) = delete;
+
+ template
+ void createIfNull(U creator) {
+ if (isNull()) {
+ reset(creator());
+ }
}
- template
- const RefPairImplementation &operator=(const QPair &other) const {
- _first = other.first;
- _second = other.second;
- return *this;
+ template
+ void makeIfNull(Args&&... args) {
+ if (isNull()) {
+ reset(new T(std::forward(args)...));
+ }
+ };
+
+ T *data() const {
+ return _p;
+ }
+ T *release() {
+ return getPointerAndReset(_p);
+ }
+ void reset(T *p = nullptr) {
+ delete _p;
+ _p = p;
+ }
+ bool isNull() const {
+ return data() == nullptr;
+ }
+
+ void clear() {
+ reset();
+ }
+ T *operator->() const {
+ return data();
+ }
+ T &operator*() const {
+ t_assert(!isNull());
+ return *data();
+ }
+ explicit operator bool() const {
+ return !isNull();
}
private:
- RefPairImplementation(T1 &first, T2 &second) : _first(first), _second(second) {
- }
- RefPairImplementation(const RefPairImplementation &other);
+ T *_p = nullptr;
- template
- friend RefPairImplementation RefPairCreator(T3 &first, T4 &second);
-
- T1 &_first;
- T2 &_second;
};
-template
-inline RefPairImplementation RefPairCreator(T1 &first, T2 &second) {
- return RefPairImplementation(first, second);
-}
+// This pointer is used for static non-POD variables that are allocated
+// on first use by constructor and are never automatically freed.
+template
+class StaticNeverFreedPointer {
+public:
+ explicit StaticNeverFreedPointer(T *p) : _p(p) {
+ }
+ StaticNeverFreedPointer(const StaticNeverFreedPointer &other) = delete;
+ StaticNeverFreedPointer &operator=(const StaticNeverFreedPointer &other) = delete;
-#define RefPair(Type1, Name1, Type2, Name2) Type1 Name1; Type2 Name2; RefPairCreator(Name1, Name2)
+ T *data() const {
+ return _p;
+ }
+ T *release() {
+ return getPointerAndReset(_p);
+ }
+ void reset(T *p = nullptr) {
+ delete _p;
+ _p = p;
+ }
+ bool isNull() const {
+ return data() == nullptr;
+ }
+
+ void clear() {
+ reset();
+ }
+ T *operator->() const {
+ return data();
+ }
+ T &operator*() const {
+ t_assert(!isNull());
+ return *data();
+ }
+ explicit operator bool() const {
+ return !isNull();
+ }
+
+private:
+ T *_p = nullptr;
+
+};
template
inline void destroyImplementation(I *&ptr) {
@@ -705,26 +944,11 @@ struct CeilDivideMinimumOne {
static const int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0));
};
-template
-struct ComponentWrapTemplate {
- static const int Size = CeilDivideMinimumOne::Result * sizeof(uint64);
- static void Construct(void *location, Composer *composer) {
- new (location) Type(composer);
- }
- static void Destruct(void *location) {
- ((Type*)location)->~Type();
- }
- static void Move(void *location, void *waslocation) {
- *(Type*)location = std11::move(*(Type*)waslocation);
- }
-};
-
extern ComponentWrapStruct ComponentWraps[64];
extern QAtomicInt ComponentIndexLast;
template
-class BaseComponent {
-public:
+struct BaseComponent {
BaseComponent() {
}
BaseComponent(const BaseComponent &other) = delete;
@@ -742,7 +966,9 @@ public:
if (ComponentIndexLast.testAndSetOrdered(last, last + 1)) {
t_assert(last < 64);
if (_index.testAndSetOrdered(0, last + 1)) {
- ComponentWraps[last] = ComponentWrapStruct(ComponentWrapTemplate::Size, ComponentWrapTemplate::Construct, ComponentWrapTemplate::Destruct, ComponentWrapTemplate::Move);
+ ComponentWraps[last] = ComponentWrapStruct(
+ CeilDivideMinimumOne::Result * sizeof(uint64),
+ Type::ComponentConstruct, Type::ComponentDestruct, Type::ComponentMove);
}
break;
}
@@ -753,6 +979,17 @@ public:
return (1ULL << Index());
}
+protected:
+ static void ComponentConstruct(void *location, Composer *composer) {
+ new (location) Type();
+ }
+ static void ComponentDestruct(void *location) {
+ ((Type*)location)->~Type();
+ }
+ static void ComponentMove(void *location, void *waslocation) {
+ *(Type*)location = std_::move(*(Type*)waslocation);
+ }
+
};
class ComposerMetadata {
@@ -848,6 +1085,21 @@ public:
}
}
+ template
+ bool Has() const {
+ return (_meta()->offsets[Type::Index()] >= 0);
+ }
+
+ template
+ Type *Get() {
+ return static_cast(_dataptr(_meta()->offsets[Type::Index()]));
+ }
+ template
+ const Type *Get() const {
+ return static_cast(_dataptr(_meta()->offsets[Type::Index()]));
+ }
+
+protected:
void UpdateComponents(uint64 mask = 0) {
if (!_meta()->equals(mask)) {
Composer tmp(mask);
@@ -870,19 +1122,6 @@ public:
UpdateComponents(_meta()->maskremove(mask));
}
- template
- Type *Get() {
- return static_cast(_dataptr(_meta()->offsets[Type::Index()]));
- }
- template
- const Type *Get() const {
- return static_cast(_dataptr(_meta()->offsets[Type::Index()]));
- }
- template
- bool Has() const {
- return (_meta()->offsets[Type::Index()] >= 0);
- }
-
private:
static const ComposerMetadata *ZeroComposerMetadata;
static void *zerodata() {
@@ -906,13 +1145,13 @@ private:
};
-template
-class SharedCallback2 {
+template
+class SharedCallback {
public:
- virtual R call(A1 channel, A2 msgId) const = 0;
- virtual ~SharedCallback2() {
+ virtual R call(Args... args) const = 0;
+ virtual ~SharedCallback() {
}
- typedef QSharedPointer > Ptr;
+ typedef QSharedPointer> Ptr;
};
template
diff --git a/Telegram/SourceFiles/core/click_handler.cpp b/Telegram/SourceFiles/core/click_handler.cpp
new file mode 100644
index 000000000..09809de14
--- /dev/null
+++ b/Telegram/SourceFiles/core/click_handler.cpp
@@ -0,0 +1,63 @@
+/*
+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 "core/click_handler.h"
+
+ClickHandlerHost::~ClickHandlerHost() {
+ ClickHandler::hostDestroyed(this);
+}
+
+NeverFreedPointer ClickHandler::_active;
+NeverFreedPointer ClickHandler::_pressed;
+ClickHandlerHost *ClickHandler::_activeHost = nullptr;
+ClickHandlerHost *ClickHandler::_pressedHost = nullptr;
+
+bool ClickHandler::setActive(const ClickHandlerPtr &p, ClickHandlerHost *host) {
+ if ((_active && (*_active == p)) || (!_active && !p)) {
+ return false;
+ }
+
+ // emit clickHandlerActiveChanged only when there is no
+ // other pressed click handler currently, if there is
+ // this method will be called when it is unpressed
+ if (_active && *_active) {
+ bool emitClickHandlerActiveChanged = (!_pressed || !*_pressed || *_pressed == *_active);
+ ClickHandlerPtr wasactive = *_active;
+ (*_active).clear();
+ if (_activeHost) {
+ if (emitClickHandlerActiveChanged) {
+ _activeHost->clickHandlerActiveChanged(wasactive, false);
+ }
+ _activeHost = nullptr;
+ }
+ }
+ if (p) {
+ _active.makeIfNull();
+ *_active = p;
+ if ((_activeHost = host)) {
+ bool emitClickHandlerActiveChanged = (!_pressed || !*_pressed || *_pressed == *_active);
+ if (emitClickHandlerActiveChanged) {
+ _activeHost->clickHandlerActiveChanged(*_active, true);
+ }
+ }
+ }
+ return true;
+}
diff --git a/Telegram/SourceFiles/core/click_handler.h b/Telegram/SourceFiles/core/click_handler.h
new file mode 100644
index 000000000..8c997e6b9
--- /dev/null
+++ b/Telegram/SourceFiles/core/click_handler.h
@@ -0,0 +1,158 @@
+/*
+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
+
+class ClickHandler;
+using ClickHandlerPtr = QSharedPointer;
+
+class ClickHandlerHost {
+protected:
+
+ virtual void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) {
+ }
+ virtual void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) {
+ }
+ virtual ~ClickHandlerHost() = 0;
+ friend class ClickHandler;
+
+};
+
+class ClickHandler {
+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() {
+ }
+
+ // this method should be called on mouse over a click handler
+ // it returns true if something was changed or false otherwise
+ static bool setActive(const ClickHandlerPtr &p, ClickHandlerHost *host = nullptr);
+
+ // this method should be called when mouse leaves the host
+ // it returns true if something was changed or false otherwise
+ static bool clearActive(ClickHandlerHost *host = nullptr) {
+ if (host && _activeHost != host) {
+ return false;
+ }
+ return setActive(ClickHandlerPtr(), host);
+ }
+
+ // this method should be called on mouse pressed
+ static void pressed() {
+ unpressed();
+ if (!_active || !*_active) {
+ return;
+ }
+ _pressed.makeIfNull();
+ *_pressed = *_active;
+ if ((_pressedHost = _activeHost)) {
+ _pressedHost->clickHandlerPressedChanged(*_pressed, true);
+ }
+ }
+
+ // this method should be called on mouse released
+ // the activated click handler is returned
+ static ClickHandlerPtr unpressed() {
+ if (_pressed && *_pressed) {
+ bool activated = (_active && *_active == *_pressed);
+ ClickHandlerPtr waspressed = *_pressed;
+ (*_pressed).clear();
+ if (_pressedHost) {
+ _pressedHost->clickHandlerPressedChanged(waspressed, false);
+ _pressedHost = nullptr;
+ }
+
+ if (activated) {
+ return *_active;
+ } else if (_active && *_active && _activeHost) {
+ // emit clickHandlerActiveChanged for current active
+ // click handler, which we didn't emit while we has
+ // a pressed click handler
+ _activeHost->clickHandlerActiveChanged(*_active, true);
+ }
+ }
+ return ClickHandlerPtr();
+ }
+
+ static ClickHandlerPtr getActive() {
+ return _active ? *_active : ClickHandlerPtr();
+ }
+ static ClickHandlerPtr getPressed() {
+ return _pressed ? *_pressed : ClickHandlerPtr();
+ }
+
+ static bool showAsActive(const ClickHandlerPtr &p) {
+ if (!p || !_active || p != *_active) {
+ return false;
+ }
+ return !_pressed || !*_pressed || (p == *_pressed);
+ }
+ static bool showAsPressed(const ClickHandlerPtr &p) {
+ if (!p || !_active || p != *_active) {
+ return false;
+ }
+ return _pressed && (p == *_pressed);
+ }
+ static void hostDestroyed(ClickHandlerHost *host) {
+ if (_activeHost == host) {
+ _activeHost = nullptr;
+ }
+ if (_pressedHost == host) {
+ _pressedHost = nullptr;
+ }
+ }
+
+private:
+
+ static NeverFreedPointer _active;
+ static NeverFreedPointer _pressed;
+ static ClickHandlerHost *_activeHost;
+ static ClickHandlerHost *_pressedHost;
+
+};
+
+class LeftButtonClickHandler : public ClickHandler {
+public:
+ void onClick(Qt::MouseButton button) const override final {
+ if (button != Qt::LeftButton) return;
+ onClickImpl();
+ }
+
+protected:
+ virtual void onClickImpl() const = 0;
+
+};
diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp
new file mode 100644
index 000000000..3c317c3dd
--- /dev/null
+++ b/Telegram/SourceFiles/core/click_handler_types.cpp
@@ -0,0 +1,133 @@
+/*
+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 "core/click_handler_types.h"
+
+#include "lang.h"
+#include "pspecific.h"
+#include "boxes/confirmbox.h"
+
+QString UrlClickHandler::copyToClipboardContextItem() const {
+ return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link);
+}
+
+namespace {
+
+QString tryConvertUrlToLocal(const QString &url) {
+ QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), QRegularExpression::CaseInsensitiveOption).match(url);
+ QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
+ QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
+ QRegularExpressionMatch telegramMeShareUrl = QRegularExpression(qsl("^https?://telegram\\.me/share/url\\?(.+)$"), QRegularExpression::CaseInsensitiveOption).match(url);
+ if (telegramMeGroup.hasMatch()) {
+ return qsl("tg://join?invite=") + myUrlEncode(telegramMeGroup.captured(1));
+ } else if (telegramMeStickers.hasMatch()) {
+ return qsl("tg://addstickers?set=") + myUrlEncode(telegramMeStickers.captured(1));
+ } else if (telegramMeShareUrl.hasMatch()) {
+ return qsl("tg://msg_url?") + telegramMeShareUrl.captured(1);
+ } else if (telegramMeUser.hasMatch()) {
+ QString params = url.mid(telegramMeUser.captured(0).size()), postParam;
+ if (QRegularExpression(qsl("^/\\d+/?(?:\\?|$)")).match(telegramMeUser.captured(2)).hasMatch()) {
+ postParam = qsl("&post=") + telegramMeUser.captured(3);
+ }
+ return qsl("tg://resolve/?domain=") + myUrlEncode(telegramMeUser.captured(1)) + postParam + (params.isEmpty() ? QString() : '&' + params);
+ }
+ return url;
+}
+
+} // namespace
+
+void UrlClickHandler::doOpen(QString url) {
+ PopupTooltip::Hide();
+
+ if (isEmail(url)) {
+ QUrl u(qstr("mailto:") + url);
+ if (!QDesktopServices::openUrl(u)) {
+ psOpenFile(u.toString(QUrl::FullyEncoded), true);
+ }
+ return;
+ }
+
+ url = tryConvertUrlToLocal(url);
+
+ if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) {
+ App::openLocalUrl(url);
+ } else {
+ QDesktopServices::openUrl(url);
+ }
+}
+
+void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const {
+ QString u = url();
+
+ u = tryConvertUrlToLocal(u);
+
+ if (u.startsWith(qstr("tg://"))) {
+ App::openLocalUrl(u);
+ } else {
+ Ui::showLayer(new ConfirmLinkBox(u));
+ }
+}
+
+QString LocationClickHandler::copyToClipboardContextItem() 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");
+}
+
+QString MentionClickHandler::copyToClipboardContextItem() const {
+ return lang(lng_context_copy_mention);
+}
+
+void MentionClickHandler::onClick(Qt::MouseButton button) const {
+ if (button == Qt::LeftButton || button == Qt::MiddleButton) {
+ App::openPeerByName(_tag.mid(1), ShowAtProfileMsgId);
+ }
+}
+
+QString HashtagClickHandler::copyToClipboardContextItem() const {
+ return lang(lng_context_copy_hashtag);
+}
+
+void HashtagClickHandler::onClick(Qt::MouseButton button) const {
+ if (button == Qt::LeftButton || button == Qt::MiddleButton) {
+ App::searchByHashtag(_tag, Ui::getPeerForMouseAction());
+ }
+}
+
+void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
+ if (button == Qt::LeftButton || button == Qt::MiddleButton) {
+ if (PeerData *peer = Ui::getPeerForMouseAction()) {
+ Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
+ App::sendBotCommand(peer, _cmd);
+ } else {
+ App::insertBotCommand(_cmd);
+ }
+ }
+}
diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h
new file mode 100644
index 000000000..9ca66ad85
--- /dev/null
+++ b/Telegram/SourceFiles/core/click_handler_types.h
@@ -0,0 +1,181 @@
+/*
+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 "core/click_handler.h"
+
+class TextClickHandler : public ClickHandler {
+public:
+
+ TextClickHandler(bool fullDisplayed = true) : _fullDisplayed(fullDisplayed) {
+ }
+
+ void copyToClipboard() const override {
+ QString u = url();
+ if (!u.isEmpty()) {
+ QApplication::clipboard()->setText(u);
+ }
+ }
+
+ QString tooltip() const override {
+ return _fullDisplayed ? QString() : readable();
+ }
+
+ void setFullDisplayed(bool full) {
+ _fullDisplayed = full;
+ }
+
+protected:
+ virtual QString url() const = 0;
+ virtual QString readable() const {
+ return url();
+ }
+
+ bool _fullDisplayed;
+
+};
+
+class UrlClickHandler : public TextClickHandler {
+public:
+ UrlClickHandler(const QString &url, bool fullDisplayed = true) : TextClickHandler(fullDisplayed), _url(url) {
+ if (isEmail()) {
+ _readable = _url;
+ } else {
+ QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString());
+ _readable = good.isValid() ? good.toDisplayString() : _url;
+ }
+ }
+ QString copyToClipboardContextItem() const override;
+
+ QString text() const override {
+ return _url;
+ }
+ QString dragText() const override {
+ return url();
+ }
+
+ static void doOpen(QString url);
+ void onClick(Qt::MouseButton button) const override {
+ if (button == Qt::LeftButton || button == Qt::MiddleButton) {
+ doOpen(url());
+ }
+ }
+
+protected:
+ QString url() const override {
+ if (isEmail()) {
+ return _url;
+ }
+
+ QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString());
+ QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _url);
+
+ if (!QRegularExpression(qsl("^[a-zA-Z]+:")).match(result).hasMatch()) { // no protocol
+ return qsl("http://") + result;
+ }
+ return result;
+ }
+ QString readable() const override {
+ return _readable;
+ }
+
+private:
+ static bool isEmail(const QString &url) {
+ int at = url.indexOf('@'), slash = url.indexOf('/');
+ return ((at > 0) && (slash < 0 || slash > at));
+ }
+ bool isEmail() const {
+ return isEmail(_url);
+ }
+
+ QString _url, _readable;
+
+};
+typedef QSharedPointer TextClickHandlerPtr;
+
+class HiddenUrlClickHandler : public UrlClickHandler {
+public:
+ HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) {
+ }
+ void onClick(Qt::MouseButton button) const override;
+
+};
+
+class MentionClickHandler : public TextClickHandler {
+public:
+ MentionClickHandler(const QString &tag) : _tag(tag) {
+ }
+ QString copyToClipboardContextItem() const override;
+
+ QString text() const override {
+ return _tag;
+ }
+ void onClick(Qt::MouseButton button) const override;
+
+protected:
+ QString url() const override {
+ return _tag;
+ }
+
+private:
+ QString _tag;
+
+};
+
+class HashtagClickHandler : public TextClickHandler {
+public:
+ HashtagClickHandler(const QString &tag) : _tag(tag) {
+ }
+ QString copyToClipboardContextItem() const override;
+
+ QString text() const override {
+ return _tag;
+ }
+ void onClick(Qt::MouseButton button) const override;
+
+protected:
+ QString url() const override {
+ return _tag;
+ }
+
+private:
+ QString _tag;
+
+};
+
+class BotCommandClickHandler : public TextClickHandler {
+public:
+ BotCommandClickHandler(const QString &cmd) : _cmd(cmd) {
+ }
+ QString text() const override {
+ return _cmd;
+ }
+ void onClick(Qt::MouseButton button) const override;
+
+protected:
+ QString url() const override {
+ return _cmd;
+ }
+
+private:
+ QString _cmd;
+
+};
diff --git a/Telegram/SourceFiles/dialogs/dialogs_common.h b/Telegram/SourceFiles/dialogs/dialogs_common.h
new file mode 100644
index 000000000..dd45fea13
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_common.h
@@ -0,0 +1,39 @@
+/*
+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
+
+namespace Dialogs {
+
+class Row;
+using RowsByLetter = QMap;
+
+enum class SortMode {
+ Date = 0x00,
+ Name = 0x01,
+ Add = 0x02,
+};
+
+enum class Mode {
+ All = 0x00,
+ Important = 0x01,
+};
+
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp
new file mode 100644
index 000000000..7c262f3aa
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp
@@ -0,0 +1,177 @@
+/*
+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 "dialogs/dialogs_indexed_list.h"
+
+namespace Dialogs {
+
+IndexedList::IndexedList(SortMode sortMode)
+: _sortMode(sortMode)
+, _list(sortMode) {
+}
+
+RowsByLetter IndexedList::addToEnd(History *history) {
+ RowsByLetter result;
+ if (!_list.contains(history->peer->id)) {
+ result.insert(0, _list.addToEnd(history));
+ for_const (auto ch, history->peer->chars) {
+ auto j = _index.find(ch);
+ if (j == _index.cend()) {
+ j = _index.insert(ch, new List(_sortMode));
+ }
+ result.insert(ch, j.value()->addToEnd(history));
+ }
+ }
+ return result;
+}
+
+Row *IndexedList::addByName(History *history) {
+ if (auto row = _list.getRow(history->peer->id)) {
+ return row;
+ }
+
+ Row *result = _list.addByName(history);
+ for_const (auto ch, history->peer->chars) {
+ auto j = _index.find(ch);
+ if (j == _index.cend()) {
+ j = _index.insert(ch, new List(_sortMode));
+ }
+ j.value()->addByName(history);
+ }
+ return result;
+}
+
+void IndexedList::adjustByPos(const RowsByLetter &links) {
+ for (auto i = links.cbegin(), e = links.cend(); i != e; ++i) {
+ if (i.key() == QChar(0)) {
+ _list.adjustByPos(i.value());
+ } else {
+ if (auto list = _index.value(i.key())) {
+ list->adjustByPos(i.value());
+ }
+ }
+ }
+}
+
+void IndexedList::peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
+ t_assert(_sortMode != SortMode::Date);
+ if (_sortMode == SortMode::Name) {
+ adjustByName(peer, oldNames, oldChars);
+ } else {
+ adjustNames(Dialogs::Mode::All, peer, oldNames, oldChars);
+ }
+}
+
+void IndexedList::peerNameChanged(Mode list, PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
+ t_assert(_sortMode == SortMode::Date);
+ adjustNames(list, peer, oldNames, oldChars);
+}
+
+void IndexedList::adjustByName(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
+ Row *mainRow = _list.adjustByName(peer);
+ if (!mainRow) return;
+
+ History *history = mainRow->history();
+
+ PeerData::NameFirstChars toRemove = oldChars, toAdd;
+ for_const (auto ch, peer->chars) {
+ auto j = toRemove.find(ch);
+ if (j == toRemove.cend()) {
+ toAdd.insert(ch);
+ } else {
+ toRemove.erase(j);
+ if (auto list = _index.value(ch)) {
+ list->adjustByName(peer);
+ }
+ }
+ }
+ for_const (auto ch, toRemove) {
+ if (auto list = _index.value(ch)) {
+ list->del(peer->id, mainRow);
+ }
+ }
+ if (!toAdd.isEmpty()) {
+ for_const (auto ch, toAdd) {
+ auto j = _index.find(ch);
+ if (j == _index.cend()) {
+ j = _index.insert(ch, new List(_sortMode));
+ }
+ j.value()->addByName(history);
+ }
+ }
+}
+
+void IndexedList::adjustNames(Mode list, PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
+ auto mainRow = _list.getRow(peer->id);
+ if (!mainRow) return;
+
+ History *history = mainRow->history();
+
+ PeerData::NameFirstChars toRemove = oldChars, toAdd;
+ for_const (auto ch, peer->chars) {
+ auto j = toRemove.find(ch);
+ if (j == toRemove.cend()) {
+ toAdd.insert(ch);
+ } else {
+ toRemove.erase(j);
+ }
+ }
+ for_const (auto ch, toRemove) {
+ if (_sortMode == SortMode::Date) {
+ history->removeChatListEntryByLetter(list, ch);
+ }
+ if (auto list = _index.value(ch)) {
+ list->del(peer->id, mainRow);
+ }
+ }
+ for_const (auto ch, toAdd) {
+ auto j = _index.find(ch);
+ if (j == _index.cend()) {
+ j = _index.insert(ch, new List(_sortMode));
+ }
+ Row *row = j.value()->addToEnd(history);
+ if (_sortMode == SortMode::Date) {
+ history->addChatListEntryByLetter(list, ch, row);
+ }
+ }
+}
+
+void IndexedList::del(const PeerData *peer, Row *replacedBy) {
+ if (_list.del(peer->id, replacedBy)) {
+ for_const (auto ch, peer->chars) {
+ if (auto list = _index.value(ch)) {
+ list->del(peer->id, replacedBy);
+ }
+ }
+ }
+}
+
+void IndexedList::clear() {
+ for_const (auto &list, _index) {
+ delete list;
+ }
+}
+
+IndexedList::~IndexedList() {
+ clear();
+}
+
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h
new file mode 100644
index 000000000..6a5967977
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.h
@@ -0,0 +1,90 @@
+/*
+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 "dialogs/dialogs_common.h"
+#include "dialogs/dialogs_list.h"
+
+class History;
+
+namespace Dialogs {
+
+class IndexedList {
+public:
+ IndexedList(SortMode sortMode);
+
+ RowsByLetter addToEnd(History *history);
+ Row *addByName(History *history);
+ void adjustByPos(const RowsByLetter &links);
+
+ // For sortMode != SortMode::Date
+ void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
+
+ //For sortMode == SortMode::Date
+ void peerNameChanged(Mode list, PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
+
+ void del(const PeerData *peer, Row *replacedBy = nullptr);
+ void clear();
+
+ const List &all() const {
+ return _list;
+ }
+ const List *filtered(QChar ch) const {
+ static StaticNeverFreedPointer empty(new List(SortMode::Add));
+ return _index.value(ch, empty.data());
+ }
+
+ ~IndexedList();
+
+ // Part of List interface is duplicated here for all() list.
+ int size() const { return all().size(); }
+ bool isEmpty() const { return all().isEmpty(); }
+ bool contains(PeerId peerId) const { return all().contains(peerId); }
+ Row *getRow(PeerId peerId) const { return all().getRow(peerId); }
+ Row *rowAtY(int32 y, int32 h) const { return all().rowAtY(y, h); }
+
+ using iterator = List::iterator;
+ using const_iterator = List::const_iterator;
+ const_iterator cbegin() const { return all().cbegin(); }
+ const_iterator cend() const { return all().cend(); }
+ const_iterator begin() const { return all().cbegin(); }
+ const_iterator end() const { return all().cend(); }
+ iterator begin() { return all().begin(); }
+ iterator end() { return all().end(); }
+ const_iterator cfind(Row *value) const { return all().cfind(value); }
+ const_iterator find(Row *value) const { return all().cfind(value); }
+ iterator find(Row *value) { return all().find(value); }
+ const_iterator cfind(int y, int h) const { return all().cfind(y, h); }
+ const_iterator find(int y, int h) const { return all().cfind(y, h); }
+ iterator find(int y, int h) { return all().find(y, h); }
+
+private:
+ void adjustByName(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
+ void adjustNames(Mode list, PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
+
+ SortMode _sortMode;
+ List _list;
+ using Index = QMap;
+ Index _index;
+
+};
+
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
new file mode 100644
index 000000000..64671379c
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
@@ -0,0 +1,276 @@
+/*
+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 "dialogs/dialogs_layout.h"
+
+#include "dialogs/dialogs_list.h"
+#include "lang.h"
+
+namespace Dialogs {
+namespace Layout {
+
+namespace {
+
+template
+void paintRow(Painter &p, History *history, HistoryItem *item, int w, bool active, bool selected, bool onlyBackground, PaintItemCallback paintItemCallback) {
+ QRect fullRect(0, 0, w, st::dlgHeight);
+ p.fillRect(fullRect, active ? st::dlgActiveBG : (selected ? st::dlgHoverBG : st::dlgBG));
+ if (onlyBackground) return;
+
+ PeerData *userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer);
+ userpicPeer->paintUserpicLeft(p, st::dlgPhotoSize, st::dlgPaddingHor, st::dlgPaddingVer, w);
+
+ int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding;
+ int32 namewidth = w - nameleft - st::dlgPaddingHor;
+ QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height);
+
+ // draw chat icon
+ if (history->peer->isChat() || history->peer->isMegagroup()) {
+ p.drawSprite(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), (active ? st::dlgActiveChatImg : st::dlgChatImg));
+ rectForName.setLeft(rectForName.left() + st::dlgImgSkip);
+ } else if (history->peer->isChannel()) {
+ p.drawSprite(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), (active ? st::dlgActiveChannelImg : st::dlgChannelImg));
+ rectForName.setLeft(rectForName.left() + st::dlgImgSkip);
+ }
+
+ if (!item) {
+ p.setFont(st::dlgHistFont);
+ p.setPen(active ? st::dlgActiveColor : st::dlgSystemColor);
+ if (history->typing.isEmpty() && history->sendActions.isEmpty()) {
+ p.drawText(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgFont->ascent + st::dlgSep, lang(lng_empty_history));
+ } else {
+ history->typingText.drawElided(p, nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth);
+ }
+ } else {
+ // draw date
+ QDateTime now(QDateTime::currentDateTime()), lastTime(item->date);
+ QDate nowDate(now.date()), lastDate(lastTime.date());
+ QString dt;
+ if (lastDate == nowDate) {
+ dt = lastTime.toString(cTimeFormat());
+ } else if (lastDate.year() == nowDate.year() && lastDate.weekNumber() == nowDate.weekNumber()) {
+ dt = langDayOfWeek(lastDate);
+ } else {
+ dt = lastDate.toString(qsl("d.MM.yy"));
+ }
+ int32 dtWidth = st::dlgDateFont->width(dt);
+ rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip);
+ p.setFont(st::dlgDateFont);
+ p.setPen(active ? st::dlgActiveDateColor : st::dlgDateColor);
+ p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt);
+
+ // draw check
+ if (item->needCheck()) {
+ const style::sprite *check;
+ if (item->id > 0) {
+ if (item->unread()) {
+ check = active ? &st::dlgActiveCheckImg : &st::dlgCheckImg;
+ } else {
+ check = active ? &st::dlgActiveDblCheckImg : &st::dlgDblCheckImg;
+ }
+ } else {
+ check = active ? &st::dlgActiveSendImg : &st::dlgSendImg;
+ }
+ rectForName.setWidth(rectForName.width() - check->pxWidth() - st::dlgCheckSkip);
+ p.drawSprite(QPoint(rectForName.left() + rectForName.width() + st::dlgCheckLeft, rectForName.top() + st::dlgCheckTop), *check);
+ }
+
+ paintItemCallback(nameleft, namewidth, item);
+ }
+
+ if (history->peer->isUser() && history->peer->isVerified()) {
+ rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x());
+ p.drawSprite(rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (active ? st::verifiedCheckInv : st::verifiedCheck));
+ }
+
+ p.setPen(active ? st::dlgActiveColor : st::dlgNameColor);
+ history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
+}
+
+class UnreadBadgeStyle : public StyleSheet {
+public:
+ QImage circle;
+ QPixmap left[4], right[4];
+ style::color bg[4] = { st::dlgUnreadBG, st::dlgActiveUnreadBG, st::dlgUnreadMutedBG, st::dlgActiveUnreadMutedBG };
+};
+StyleSheetPointer unreadBadgeStyle;
+
+void createCircleMask(int size) {
+ if (!unreadBadgeStyle->circle.isNull()) return;
+
+ unreadBadgeStyle->circle = QImage(size, size, QImage::Format::Format_Grayscale8);
+ {
+ QPainter pcircle(&unreadBadgeStyle->circle);
+ pcircle.setRenderHint(QPainter::HighQualityAntialiasing, true);
+ pcircle.fillRect(0, 0, size, size, QColor(0, 0, 0));
+ pcircle.setPen(Qt::NoPen);
+ pcircle.setBrush(QColor(255, 255, 255));
+ pcircle.drawEllipse(0, 0, size, size);
+ }
+ unreadBadgeStyle->circle.setDevicePixelRatio(cRetinaFactor());
+}
+
+QImage colorizeCircleHalf(int size, int half, int xoffset, style::color color) {
+ int a = color->c.alpha() + 1;
+ int fg_r = color->c.red() * a, fg_g = color->c.green() * a, fg_b = color->c.blue() * a, fg_a = 255 * a;
+
+ QImage result(size, size, QImage::Format_ARGB32_Premultiplied);
+ uchar *bits = result.bits(), *maskbits = unreadBadgeStyle->circle.bits();
+ int bpl = result.bytesPerLine(), maskbpl = unreadBadgeStyle->circle.bytesPerLine();
+ for (int x = 0; x < size; ++x) {
+ for (int y = 0; y < size; ++y) {
+ int s = y * bpl + (x * 4);
+ int o = maskbits[y * maskbpl + x + xoffset] + 1;
+ bits[s + 0] = (fg_b * o) >> 16;
+ bits[s + 1] = (fg_g * o) >> 16;
+ bits[s + 2] = (fg_r * o) >> 16;
+ bits[s + 3] = (fg_a * o) >> 16;
+ }
+ }
+ result.setDevicePixelRatio(cRetinaFactor());
+ return result;
+}
+
+void paintUnreadBadge(Painter &p, const QRect &rect, bool active, bool muted) {
+ int index = (active ? 0x01 : 0x00) | (muted ? 0x02 : 0x00);
+ int size = rect.height(), sizehalf = size / 2;
+
+ unreadBadgeStyle.createIfNull();
+ style::color bg = unreadBadgeStyle->bg[index];
+ if (unreadBadgeStyle->left[index].isNull()) {
+ int imgsize = size * cIntRetinaFactor(), imgsizehalf = sizehalf * cIntRetinaFactor();
+ createCircleMask(imgsize);
+ unreadBadgeStyle->left[index] = QPixmap::fromImage(colorizeCircleHalf(imgsize, imgsizehalf, 0, bg));
+ unreadBadgeStyle->right[index] = QPixmap::fromImage(colorizeCircleHalf(imgsize, imgsizehalf, imgsize - imgsizehalf, bg));
+ }
+
+ int bar = rect.width() - 2 * sizehalf;
+ p.drawPixmap(rect.x(), rect.y(), unreadBadgeStyle->left[index]);
+ if (bar) {
+ p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), bg);
+ }
+ p.drawPixmap(rect.x() + sizehalf + bar, rect.y(), unreadBadgeStyle->right[index]);
+}
+
+void paintUnreadCount(Painter &p, const QString &text, int top, int w, bool active, bool muted, int *outAvailableWidth) {
+ int unreadWidth = st::dlgUnreadFont->width(text);
+ int unreadRectWidth = unreadWidth + 2 * st::dlgUnreadPaddingHor;
+ int unreadRectHeight = st::dlgUnreadHeight;
+ accumulate_max(unreadRectWidth, unreadRectHeight);
+
+ int unreadRectLeft = w - st::dlgPaddingHor - unreadRectWidth;
+ int unreadRectTop =top;
+ if (outAvailableWidth) {
+ *outAvailableWidth -= unreadRectWidth + st::dlgUnreadPaddingHor;
+ }
+
+ paintUnreadBadge(p, QRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight), active, muted);
+
+ p.setFont(st::dlgUnreadFont);
+ p.setPen(active ? st::dlgActiveUnreadColor : st::dlgUnreadColor);
+ p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + st::dlgUnreadTop + st::dlgUnreadFont->ascent, text);
+}
+
+} // namepsace
+
+void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground) {
+ auto history = row->history();
+ auto item = history->lastMsg;
+ paintRow(p, history, item, w, active, selected, onlyBackground, [&p, w, active, history](int nameleft, int namewidth, HistoryItem *item) {
+ int32 unread = history->unreadCount();
+ if (history->peer->migrateFrom()) {
+ if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) {
+ unread += h->unreadCount();
+ }
+ }
+ int availableWidth = namewidth;
+ int texttop = st::dlgPaddingVer + st::dlgFont->height + st::dlgSep;
+ if (unread) {
+ int unreadTop = texttop + st::dlgHistFont->ascent - st::dlgUnreadFont->ascent - st::dlgUnreadTop;
+ paintUnreadCount(p, QString::number(unread), unreadTop, w, active, history->mute(), &availableWidth);
+ }
+ if (history->typing.isEmpty() && history->sendActions.isEmpty()) {
+ item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dlgFont->height), active, history->textCachedFor, history->lastItemTextCache);
+ } else {
+ p.setPen(active ? st::dlgActiveColor : st::dlgSystemColor);
+ history->typingText.drawElided(p, nameleft, texttop, availableWidth);
+ }
+ });
+}
+
+void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground) {
+ auto item = row->item();
+ auto history = item->history();
+ paintRow(p, history, item, w, active, selected, onlyBackground, [&p, row, active](int nameleft, int namewidth, HistoryItem *item) {
+ int lastWidth = namewidth, texttop = st::dlgPaddingVer + st::dlgFont->height + st::dlgSep;
+ item->drawInDialog(p, QRect(nameleft, texttop, lastWidth, st::dlgFont->height), active, row->_cacheFor, row->_cache);
+ });
+}
+
+void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground) {
+ p.fillRect(0, 0, w, st::dlgImportantHeight, selected ? st::dlgHoverBG : st::white);
+ if (onlyBackground) {
+ return;
+ }
+
+ p.setFont(st::semiboldFont);
+ p.setPen(st::black);
+
+ int unreadTop = (st::dlgImportantHeight - st::dlgUnreadHeight) / 2;
+ bool mutedHidden = (current == Dialogs::Mode::Important);
+ QString text = mutedHidden ? qsl("Show all chats") : qsl("Hide muted chats");
+ int textBaseline = unreadTop + st::dlgUnreadTop + st::dlgUnreadFont->ascent;
+ p.drawText(st::dlgPaddingHor, textBaseline, text);
+
+ if (mutedHidden) {
+ if (int32 unread = App::histories().unreadMutedCount()) {
+ paintUnreadCount(p, QString::number(unread), unreadTop, w, false, true, nullptr);
+ }
+ }
+}
+
+namespace {
+
+using StyleSheets = OrderedSet;
+NeverFreedPointer styleSheets;
+
+}
+
+namespace internal {
+
+void registerStyleSheet(StyleSheet **p) {
+ styleSheets.makeIfNull();
+ styleSheets->insert(p);
+}
+
+} // namespace internal
+
+void clearStyleSheets() {
+ if (!styleSheets) return;
+ for (auto &p : *styleSheets) {
+ delete (*p);
+ *p = nullptr;
+ }
+ styleSheets.clear();
+}
+
+} // namespace Layout
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h
new file mode 100644
index 000000000..1c0de56a1
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h
@@ -0,0 +1,78 @@
+/*
+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
+
+namespace Dialogs {
+
+class Row;
+class FakeRow;
+
+namespace Layout {
+
+class RowPainter {
+public:
+ static void paint(Painter &p, const Row *row, int w, bool active, bool selected, bool onlyBackground);
+ static void paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground);
+};
+
+void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground);
+
+// This will be moved somewhere outside as soon as anyone starts using that.
+class StyleSheet {
+public:
+ virtual ~StyleSheet() = 0;
+};
+inline StyleSheet::~StyleSheet() = default;
+
+namespace internal {
+
+void registerStyleSheet(StyleSheet **p);
+
+} // namespace
+
+// Must be created in global scope!
+template
+class StyleSheetPointer {
+public:
+ StyleSheetPointer() = default;
+ StyleSheetPointer(const StyleSheetPointer &other) = delete;
+ StyleSheetPointer &operator=(const StyleSheetPointer &other) = delete;
+
+ void createIfNull() {
+ if (!_p) {
+ _p = new T();
+ internal::registerStyleSheet(&_p);
+ }
+ }
+ T *operator->() {
+ t_assert(_p != nullptr);
+ return static_cast(_p);
+ }
+
+private:
+ StyleSheet *_p;
+
+};
+
+void clearStyleSheets();
+
+} // namespace Layout
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp
new file mode 100644
index 000000000..12591987a
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp
@@ -0,0 +1,239 @@
+/*
+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 "dialogs/dialogs_list.h"
+
+#include "dialogs/dialogs_layout.h"
+#include "mainwidget.h"
+
+namespace Dialogs {
+
+List::List(SortMode sortMode)
+: _last(std_::make_unique(nullptr, nullptr, nullptr, 0))
+, _begin(_last.get())
+, _end(_last.get())
+, _sortMode(sortMode)
+, _current(_last.get()) {
+}
+
+void List::adjustCurrent(int32 y, int32 h) const {
+ if (isEmpty()) return;
+
+ int32 pos = (y > 0) ? (y / h) : 0;
+ while (_current->_pos > pos && _current != _begin) {
+ _current = _current->_prev;
+ }
+ while (_current->_pos + 1 <= pos && _current->_next != _end) {
+ _current = _current->_next;
+ }
+}
+
+void List::paint(Painter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel, bool onlyBackground) const {
+ adjustCurrent(hFrom, st::dlgHeight);
+
+ Row *row = _current;
+ p.translate(0, row->_pos * st::dlgHeight);
+ while (row != _end && row->_pos * st::dlgHeight < hTo) {
+ bool active = (row->history()->peer == act) || (row->history()->peer->migrateTo() && row->history()->peer->migrateTo() == act);
+ bool selected = (row->history()->peer == sel);
+ Layout::RowPainter::paint(p, row, w, active, selected, onlyBackground);
+ row = row->_next;
+ p.translate(0, st::dlgHeight);
+ }
+}
+
+Row *List::addToEnd(History *history) {
+ Row *result = new Row(history, _end->_prev, _end, _end->_pos);
+ _end->_pos++;
+ if (_begin == _end) {
+ _begin = _current = result;
+ } else {
+ _end->_prev->_next = result;
+ }
+ _rowByPeer.insert(history->peer->id, result);
+ ++_count;
+ _end->_prev = result;
+ if (_sortMode == SortMode::Date) {
+ adjustByPos(result);
+ }
+ return result;
+}
+
+bool List::insertBefore(Row *row, Row *before) {
+ if (row == before) return false;
+
+ if (_current == row) {
+ _current = row->_prev;
+ }
+
+ Row *updateTill = row->_prev;
+ remove(row);
+
+ // insert row
+ row->_next = before; // update row
+ row->_prev = before->_prev;
+ row->_next->_prev = row; // update row->next
+ if (row->_prev) { // update row->prev
+ row->_prev->_next = row;
+ } else {
+ _begin = row;
+ }
+
+ // update y
+ for (Row *n = row; n != updateTill; n = n->_next) {
+ n->_next->_pos++;
+ row->_pos--;
+ }
+ return true;
+}
+
+bool List::insertAfter(Row *row, Row *after) {
+ if (row == after) return false;
+
+ if (_current == row) {
+ _current = row->_next;
+ }
+
+ Row *updateFrom = row->_next;
+ remove(row);
+
+ // insert row
+ row->_prev = after; // update row
+ row->_next = after->_next;
+ row->_prev->_next = row; // update row->prev
+ row->_next->_prev = row; // update row->next
+
+ // update y
+ for (Row *n = updateFrom; n != row; n = n->_next) {
+ n->_pos--;
+ row->_pos++;
+ }
+ return true;
+}
+
+Row *List::adjustByName(const PeerData *peer) {
+ if (_sortMode != SortMode::Name) return nullptr;
+
+ auto i = _rowByPeer.find(peer->id);
+ if (i == _rowByPeer.cend()) return nullptr;
+
+ Row *row = i.value(), *change = row;
+ while (change->_prev && change->_prev->history()->peer->name > peer->name) {
+ change = change->_prev;
+ }
+ if (!insertBefore(row, change)) {
+ while (change->_next != _end && change->_next->history()->peer->name < peer->name) {
+ change = change->_next;
+ }
+ insertAfter(row, change);
+ }
+ return row;
+}
+
+Row *List::addByName(History *history) {
+ if (_sortMode != SortMode::Name) return nullptr;
+
+ Row *row = addToEnd(history), *change = row;
+ const QString &peerName(history->peer->name);
+ while (change->_prev && change->_prev->history()->peer->name.compare(peerName, Qt::CaseInsensitive) > 0) {
+ change = change->_prev;
+ }
+ if (!insertBefore(row, change)) {
+ while (change->_next != _end && change->_next->history()->peer->name.compare(peerName, Qt::CaseInsensitive) < 0) {
+ change = change->_next;
+ }
+ insertAfter(row, change);
+ }
+ return row;
+}
+
+void List::adjustByPos(Row *row) {
+ if (_sortMode != SortMode::Date || !_begin) return;
+
+ Row *change = row;
+ if (change != _begin && _begin->history()->sortKeyInChatList() < row->history()->sortKeyInChatList()) {
+ change = _begin;
+ } else {
+ while (change->_prev && change->_prev->history()->sortKeyInChatList() < row->history()->sortKeyInChatList()) {
+ change = change->_prev;
+ }
+ }
+ if (!insertBefore(row, change)) {
+ if (change->_next != _end && _end->_prev->history()->sortKeyInChatList() > row->history()->sortKeyInChatList()) {
+ change = _end->_prev;
+ } else {
+ while (change->_next != _end && change->_next->history()->sortKeyInChatList() > row->history()->sortKeyInChatList()) {
+ change = change->_next;
+ }
+ }
+ insertAfter(row, change);
+ }
+}
+
+bool List::del(PeerId peerId, Row *replacedBy) {
+ auto i = _rowByPeer.find(peerId);
+ if (i == _rowByPeer.cend()) return false;
+
+ Row *row = i.value();
+ if (App::main()) {
+ emit App::main()->dialogRowReplaced(row, replacedBy);
+ }
+
+ if (row == _current) {
+ _current = row->_next;
+ }
+ for (Row *change = row->_next; change != _end; change = change->_next) {
+ --change->_pos;
+ }
+ --_end->_pos;
+ remove(row);
+ delete row;
+ --_count;
+ _rowByPeer.erase(i);
+
+ return true;
+}
+
+void List::remove(Row *row) {
+ row->_next->_prev = row->_prev; // update row->next
+ if (row->_prev) { // update row->prev
+ row->_prev->_next = row->_next;
+ } else {
+ _begin = row->_next;
+ }
+}
+
+void List::clear() {
+ while (_begin != _end) {
+ _current = _begin;
+ _begin = _begin->_next;
+ delete _current;
+ }
+ _current = _begin;
+ _rowByPeer.clear();
+ _count = 0;
+}
+
+List::~List() {
+ clear();
+}
+
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.h b/Telegram/SourceFiles/dialogs/dialogs_list.h
new file mode 100644
index 000000000..422eeb203
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_list.h
@@ -0,0 +1,136 @@
+/*
+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 "dialogs/dialogs_row.h"
+
+class PeerData;
+namespace Dialogs {
+
+class List {
+public:
+ List(SortMode sortMode);
+ List(const List &other) = delete;
+ List &operator=(const List &other) = delete;
+
+ int size() const {
+ return _count;
+ }
+ bool isEmpty() const {
+ return size() == 0;
+ }
+ bool contains(PeerId peerId) const {
+ return _rowByPeer.contains(peerId);
+ }
+ Row *getRow(PeerId peerId) const {
+ return _rowByPeer.value(peerId);
+ }
+ Row *rowAtY(int32 y, int32 h) const {
+ auto i = cfind(y, h);
+ if (i == cend() || (*i)->pos() != ((y > 0) ? (y / h) : 0)) {
+ return nullptr;
+ }
+ return *i;
+ }
+
+ void paint(Painter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel, bool onlyBackground) const;
+ Row *addToEnd(History *history);
+ bool insertBefore(Row *row, Row *before);
+ bool insertAfter(Row *row, Row *after);
+ Row *adjustByName(const PeerData *peer);
+ Row *addByName(History *history);
+ void adjustByPos(Row *row);
+ bool del(PeerId peerId, Row *replacedBy = nullptr);
+ void remove(Row *row);
+ void clear();
+
+ class const_iterator {
+ public:
+ using value_type = Row*;
+ using pointer = Row**;
+ using reference = Row*&;
+
+ explicit const_iterator(Row *p) : _p(p) {
+ }
+ inline Row* operator*() const { return _p; }
+ inline Row* const* operator->() const { return &_p; }
+ inline bool operator==(const const_iterator &other) const { return _p == other._p; }
+ inline bool operator!=(const const_iterator &other) const { return !(*this == other); }
+ inline const_iterator &operator++() { _p = next(_p); return *this; }
+ inline const_iterator operator++(int) { const_iterator result(*this); ++(*this); return result; }
+ inline const_iterator &operator--() { _p = prev(_p); return *this; }
+ inline const_iterator operator--(int) { const_iterator result(*this); --(*this); return result; }
+ inline const_iterator operator+(int j) const { const_iterator result = *this; return result += j; }
+ inline const_iterator operator-(int j) const { const_iterator result = *this; return result -= j; }
+ inline const_iterator &operator+=(int j) { if (j < 0) return (*this -= (-j)); while (j--) ++*this; return *this; }
+ inline const_iterator &operator-=(int j) { if (j < 0) return (*this += (-j)); while (j--) --*this; return *this; }
+
+ private:
+ Row *_p;
+ friend class List;
+
+ };
+ friend class const_iterator;
+ using iterator = const_iterator;
+
+ const_iterator cbegin() const { return const_iterator(_begin); }
+ const_iterator cend() const { return const_iterator(_end); }
+ const_iterator begin() const { return cbegin(); }
+ const_iterator end() const { return cend(); }
+ iterator begin() { return iterator(_begin); }
+ iterator end() { return iterator(_end); }
+ const_iterator cfind(Row *value) const { return value ? const_iterator(value) : cend(); }
+ const_iterator find(Row *value) const { return cfind(value); }
+ iterator find(Row *value) { return value ? iterator(value) : end(); }
+ const_iterator cfind(int y, int h) const {
+ adjustCurrent(y, h);
+ return iterator(_current);
+ }
+ const_iterator find(int y, int h) const { return cfind(y, h); }
+ iterator find(int y, int h) {
+ adjustCurrent(y, h);
+ return iterator(_current);
+ }
+
+ ~List();
+
+private:
+ void adjustCurrent(int y, int h) const;
+ static Row *next(Row *row) {
+ return row->_next;
+ }
+ static Row *prev(Row *row) {
+ return row->_prev;
+ }
+
+ std_::unique_ptr _last;
+ Row *_begin;
+ Row *_end;
+ SortMode _sortMode;
+ int _count = 0;
+
+ typedef QHash RowByPeer;
+ RowByPeer _rowByPeer;
+
+ mutable Row *_current; // cache
+};
+
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h
new file mode 100644
index 000000000..88dd3ca6f
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_row.h
@@ -0,0 +1,78 @@
+/*
+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/text.h"
+
+class History;
+class HistoryItem;
+
+namespace Dialogs {
+namespace Layout {
+class RowPainter;
+} // namespace Layout
+
+class List;
+class Row {
+public:
+ Row(History *history, Row *prev, Row *next, int pos)
+ : _history(history)
+ , _prev(prev)
+ , _next(next)
+ , _pos(pos) {
+ }
+ void *attached = nullptr; // for any attached data, for example View in contacts list
+
+ History *history() const {
+ return _history;
+ }
+ int pos() const {
+ return _pos;
+ }
+
+private:
+ friend class List;
+
+ History *_history;
+ Row *_prev, *_next;
+ int _pos;
+
+};
+
+class FakeRow {
+public:
+ FakeRow(HistoryItem *item) : _item(item) {
+ }
+
+ HistoryItem *item() const {
+ return _item;
+ }
+
+private:
+ friend class Layout::RowPainter;
+
+ HistoryItem *_item;
+ mutable const HistoryItem *_cacheFor = nullptr;
+ mutable Text _cache = Text{ int(st::dlgRichMinWidth) };
+
+};
+
+} // namespace Dialogs
diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp
index 15c6d61b4..1f0aa382f 100644
--- a/Telegram/SourceFiles/dialogswidget.cpp
+++ b/Telegram/SourceFiles/dialogswidget.cpp
@@ -19,65 +19,55 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
-#include "style.h"
-#include "lang.h"
+#include "dialogswidget.h"
+#include "dialogs/dialogs_indexed_list.h"
+#include "dialogs/dialogs_layout.h"
+#include "ui/style.h"
+#include "lang.h"
#include "application.h"
-#include "window.h"
+#include "mainwindow.h"
#include "dialogswidget.h"
#include "mainwidget.h"
#include "boxes/addcontactbox.h"
#include "boxes/contactsbox.h"
#include "boxes/confirmbox.h"
-
#include "localstorage.h"
+#include "apiwrap.h"
DialogsInner::DialogsInner(QWidget *parent, MainWidget *main) : SplittedWidget(parent)
-, dialogs(DialogsSortByDate)
-, contactsNoDialogs(DialogsSortByName)
-, contacts(DialogsSortByName)
-, sel(0)
-, contactSel(false)
-, selByMouse(false)
-, _hashtagSel(-1)
-, _filteredSel(-1)
-, _searchedCount(0)
-, _searchedMigratedCount(0)
-, _searchedSel(-1)
-, _peopleSel(-1)
-, _lastSearchDate(0)
-, _lastSearchPeer(0)
-, _lastSearchId(0)
-, _lastSearchMigratedId(0)
-, _state(DefaultState)
+, dialogs(std_::make_unique(Dialogs::SortMode::Date))
+, contactsNoDialogs(std_::make_unique(Dialogs::SortMode::Name))
+, contacts(std_::make_unique(Dialogs::SortMode::Name))
, _addContactLnk(this, lang(lng_add_contact_button))
-, _cancelSearchInPeer(this, st::btnCancelSearch)
-, _overDelete(false)
-, _searchInPeer(0)
-, _searchInMigrated(0)
-, _menuPeer(0)
-, _menuActionPeer(0)
-, _menu(0) {
+, _cancelSearchInPeer(this, st::btnCancelSearch) {
+ if (Global::DialogsModeEnabled()) {
+ importantDialogs = std_::make_unique(Dialogs::SortMode::Date);
+ }
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
connect(main, SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)));
connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*)));
- connect(main, SIGNAL(dialogRowReplaced(DialogRow*,DialogRow*)), this, SLOT(onDialogRowReplaced(DialogRow*,DialogRow*)));
+ connect(main, SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*)));
connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
connect(&_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer()));
_cancelSearchInPeer.hide();
refresh();
}
-int32 DialogsInner::filteredOffset() const {
+int DialogsInner::dialogsOffset() const {
+ return importantDialogs ? st::dlgImportantHeight : 0;
+}
+
+int DialogsInner::filteredOffset() const {
return _hashtagResults.size() * st::mentionHeight;
}
-int32 DialogsInner::peopleOffset() const {
+int DialogsInner::peopleOffset() const {
return filteredOffset() + (_filterResults.size() * st::dlgHeight) + st::searchedBarHeight;
}
-int32 DialogsInner::searchedOffset() const {
- int32 result = peopleOffset() + (_peopleResults.isEmpty() ? 0 : ((_peopleResults.size() * st::dlgHeight) + st::searchedBarHeight));
+int DialogsInner::searchedOffset() const {
+ int result = peopleOffset() + (_peopleResults.isEmpty() ? 0 : ((_peopleResults.size() * st::dlgHeight) + st::searchedBarHeight));
if (_searchInPeer) result += st::dlgHeight;
return result;
}
@@ -94,18 +84,22 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
}
if (_state == DefaultState) {
- int32 otherStart = dialogs.list.count * st::dlgHeight;
- PeerData *active = App::main()->activePeer(), *selected = _menuPeer ? _menuPeer : (sel ? sel->history->peer : 0);
- if (otherStart) {
- dialogs.list.paint(p, fullWidth(), r.top(), r.top() + r.height(), active, selected, paintingOther);
+ QRect dialogsClip = r;
+ if (importantDialogs) {
+ Dialogs::Layout::paintImportantSwitch(p, Global::DialogsMode(), fullWidth(), _importantSwitchSel, paintingOther);
+ dialogsClip.translate(0, -st::dlgImportantHeight);
+ p.translate(0, st::dlgImportantHeight);
}
- if (contactsNoDialogs.list.count && false) {
- contactsNoDialogs.list.paint(p, fullWidth(), r.top() - otherStart, r.top() + r.height() - otherStart, active, selected, paintingOther);
- } else if (!otherStart) {
- p.fillRect(r, st::white->b);
+ int32 otherStart = shownDialogs()->size() * st::dlgHeight;
+ PeerData *active = App::main()->activePeer(), *selected = _menuPeer ? _menuPeer : (_sel ? _sel->history()->peer : 0);
+ if (otherStart) {
+ shownDialogs()->all().paint(p, fullWidth(), dialogsClip.top(), dialogsClip.top() + dialogsClip.height(), active, selected, paintingOther);
+ }
+ if (!otherStart) {
+ p.fillRect(dialogsClip, st::white);
if (!paintingOther) {
- p.setFont(st::noContactsFont->f);
- p.setPen(st::noContactsColor->p);
+ p.setFont(st::noContactsFont);
+ p.setPen(st::noContactsColor);
p.drawText(QRect(0, 0, fullWidth(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_chats : lng_contacts_loading), style::al_center);
}
}
@@ -163,9 +157,9 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
PeerData *act = App::main()->activePeer();
MsgId actId = App::main()->activeMsgId();
for (; from < to; ++from) {
- bool active = ((_filterResults[from]->history->peer == act) || (_filterResults[from]->history->peer->migrateTo() && _filterResults[from]->history->peer->migrateTo() == act)) && !actId;
- bool selected = (from == _filteredSel) || (_filterResults[from]->history->peer == _menuPeer);
- _filterResults[from]->paint(p, w, active, selected, paintingOther);
+ bool active = ((_filterResults[from]->history()->peer == act) || (_filterResults[from]->history()->peer->migrateTo() && _filterResults[from]->history()->peer->migrateTo() == act)) && !actId;
+ bool selected = (from == _filteredSel) || (_filterResults[from]->history()->peer == _menuPeer);
+ Dialogs::Layout::RowPainter::paint(p, _filterResults[from], w, active, selected, paintingOther);
p.translate(0, st::dlgHeight);
}
}
@@ -230,9 +224,12 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
PeerData *act = App::main()->activePeer();
MsgId actId = App::main()->activeMsgId();
for (; from < to; ++from) {
- bool active = (_searchResults[from]->_item->history()->peer == act && _searchResults[from]->_item->id == actId) || (_searchResults[from]->_item->history()->peer->migrateTo() && _searchResults[from]->_item->history()->peer->migrateTo() == act && _searchResults[from]->_item->id == -actId);
+ auto result = _searchResults[from];
+ auto item = result->item();
+ auto history = item->history();
+ bool active = (history->peer == act && item->id == actId) || (history->peer->migrateTo() && history->peer->migrateTo() == act && item->id == -actId);
bool selected = (from == _searchedSel);
- _searchResults[from]->paint(p, w, active, selected, paintingOther);
+ Dialogs::Layout::RowPainter::paint(p, result, w, active, selected, paintingOther);
p.translate(0, st::dlgHeight);
}
}
@@ -240,13 +237,11 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
}
}
-void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const {
+void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool active, bool selected, bool onlyBackground) const {
QRect fullRect(0, 0, w, st::dlgHeight);
- p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b);
+ p.fillRect(fullRect, active ? st::dlgActiveBG : (selected ? st::dlgHoverBG : st::dlgBG));
if (onlyBackground) return;
- History *history = App::history(peer->id);
-
PeerData *userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer);
userpicPeer->paintUserpicLeft(p, st::dlgPhotoSize, st::dlgPaddingHor, st::dlgPaddingVer, fullWidth());
@@ -256,21 +251,21 @@ void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool a
// draw chat icon
if (peer->isChat() || peer->isMegagroup()) {
- p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), App::sprite(), (act ? st::dlgActiveChatImg : st::dlgChatImg));
+ p.drawSprite(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), (active ? st::dlgActiveChatImg : st::dlgChatImg));
rectForName.setLeft(rectForName.left() + st::dlgImgSkip);
} else if (peer->isChannel()) {
- p.drawPixmap(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), App::sprite(), (act ? st::dlgActiveChannelImg : st::dlgChannelImg));
+ p.drawSprite(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), (active ? st::dlgActiveChannelImg : st::dlgChannelImg));
rectForName.setLeft(rectForName.left() + st::dlgImgSkip);
}
if (peer->isVerified()) {
rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x());
- p.drawSprite(rectForName.topLeft() + QPoint(qMin(peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (act ? st::verifiedCheckInv : st::verifiedCheck));
+ p.drawSprite(rectForName.topLeft() + QPoint(qMin(peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (active ? st::verifiedCheckInv : st::verifiedCheck));
}
QRect tr(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth, st::dlgFont->height);
p.setFont(st::dlgHistFont->f);
QString username = peer->userName();
- if (!act && username.toLower().startsWith(_peopleQuery)) {
+ if (!active && username.toLower().startsWith(_peopleQuery)) {
QString first = '@' + username.mid(0, _peopleQuery.size()), second = username.mid(_peopleQuery.size());
int32 w = st::dlgHistFont->width(first);
if (w >= tr.width()) {
@@ -283,11 +278,11 @@ void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool a
p.drawText(tr.left() + w, tr.top() + st::dlgHistFont->ascent, st::dlgHistFont->elided(second, tr.width() - w));
}
} else {
- p.setPen((act ? st::dlgActiveColor : st::dlgSystemColor)->p);
+ p.setPen((active ? st::dlgActiveColor : st::dlgSystemColor)->p);
p.drawText(tr.left(), tr.top() + st::dlgHistFont->ascent, st::dlgHistFont->elided('@' + username, tr.width()));
}
- p.setPen((act ? st::dlgActiveColor : st::dlgNameColor)->p);
+ p.setPen((active ? st::dlgActiveColor : st::dlgNameColor)->p);
peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
}
@@ -325,30 +320,26 @@ void DialogsInner::activate() {
void DialogsInner::mouseMoveEvent(QMouseEvent *e) {
lastMousePos = mapToGlobal(e->pos());
- selByMouse = true;
+ _selByMouse = true;
onUpdateSelected(true);
}
void DialogsInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(lastMousePos));
- if ((!force && !rect().contains(mouse)) || !selByMouse) return;
+ if ((!force && !rect().contains(mouse)) || !_selByMouse) return;
int w = width(), mouseY = mouse.y();
_overDelete = false;
if (_state == DefaultState) {
- DialogRow *newSel = dialogs.list.rowAtY(mouseY, st::dlgHeight);
- int32 otherStart = dialogs.list.count * st::dlgHeight;
- if (newSel) {
- contactSel = false;
- } else {
- newSel = 0;// contactsNoDialogs.list.rowAtY(mouseY - otherStart, st::dlgHeight);
- contactSel = true;
- }
- if (newSel != sel) {
+ auto newImportantSwitchSel = (importantDialogs && mouseY >= 0 && mouseY < dialogsOffset());
+ mouseY -= dialogsOffset();
+ auto newSel = newImportantSwitchSel ? nullptr : shownDialogs()->rowAtY(mouseY, st::dlgHeight);
+ if (newSel != _sel || newImportantSwitchSel != _importantSwitchSel) {
updateSelectedRow();
- sel = newSel;
+ _sel = newSel;
+ _importantSwitchSel = newImportantSwitchSel;
updateSelectedRow();
- setCursor(sel ? style::cur_pointer : style::cur_default);
+ setCursor(_sel ? style::cur_pointer : style::cur_default);
}
} else if (_state == FilteredState || _state == SearchedState) {
if (!_hashtagResults.isEmpty()) {
@@ -407,7 +398,7 @@ void DialogsInner::onUpdateSelected(bool force) {
void DialogsInner::mousePressEvent(QMouseEvent *e) {
lastMousePos = mapToGlobal(e->pos());
- selByMouse = true;
+ _selByMouse = true;
onUpdateSelected(true);
if (e->button() == Qt::LeftButton) {
choosePeer();
@@ -419,7 +410,7 @@ void DialogsInner::resizeEvent(QResizeEvent *e) {
_cancelSearchInPeer.move(width() - st::dlgPaddingHor - st::btnCancelSearch.width, (st::dlgHeight - st::btnCancelSearch.height) / 2);
}
-void DialogsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
+void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) {
if (_state == FilteredState || _state == SearchedState) {
for (FilteredDialogs::iterator i = _filterResults.begin(); i != _filterResults.end();) {
if (*i == oldRow) { // this row is shown in filtered and maybe is in contacts!
@@ -434,25 +425,47 @@ void DialogsInner::onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow) {
}
}
}
- if (sel == oldRow) {
- sel = newRow;
+ if (_sel == oldRow) {
+ _sel = newRow;
}
}
void DialogsInner::createDialog(History *history) {
- bool creating = !history->inChatList();
+ bool creating = !history->inChatList(Dialogs::Mode::All);
if (creating) {
- DialogRow *mainRow = history->addToChatList(dialogs);
- contactsNoDialogs.del(history->peer, mainRow);
+ Dialogs::Row *mainRow = history->addToChatList(Dialogs::Mode::All, dialogs.get());
+ contactsNoDialogs->del(history->peer, mainRow);
+ }
+ if (importantDialogs && !history->inChatList(Dialogs::Mode::Important) && !history->mute()) {
+ if (Global::DialogsMode() == Dialogs::Mode::Important) {
+ creating = true;
+ }
+ history->addToChatList(Dialogs::Mode::Important, importantDialogs.get());
}
- RefPair(int32, movedFrom, int32, movedTo) = history->adjustByPosInChatsList(dialogs);
- emit dialogMoved(movedFrom, movedTo);
+ auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, dialogs.get());
+
+ if (importantDialogs) {
+ if (history->mute()) {
+ if (Global::DialogsMode() == Dialogs::Mode::Important) {
+ return;
+ }
+ } else {
+ auto importantChanged = history->adjustByPosInChatList(Dialogs::Mode::Important, importantDialogs.get());
+ if (Global::DialogsMode() == Dialogs::Mode::Important) {
+ changed = importantChanged;
+ }
+ }
+ }
+
+ int from = dialogsOffset() + changed.movedFrom * st::dlgHeight;
+ int to = dialogsOffset() + changed.movedTo * st::dlgHeight;
+ emit dialogMoved(from, to);
if (creating) {
refresh();
- } else if (_state == DefaultState && movedFrom != movedTo) {
- update(0, qMin(movedFrom, movedTo), fullWidth(), qAbs(movedFrom - movedTo) + st::dlgHeight);
+ } else if (_state == DefaultState && changed.movedFrom != changed.movedTo) {
+ update(0, qMin(from, to), fullWidth(), qAbs(from - to) + st::dlgHeight);
}
}
@@ -461,15 +474,18 @@ void DialogsInner::removeDialog(History *history) {
if (history->peer == _menuPeer && _menu) {
_menu->deleteLater();
}
- if (sel && sel->history == history) {
- sel = 0;
+ if (_sel && _sel->history() == history) {
+ _sel = nullptr;
+ }
+ history->removeFromChatList(Dialogs::Mode::All, dialogs.get());
+ if (importantDialogs) {
+ history->removeFromChatList(Dialogs::Mode::Important, importantDialogs.get());
}
- history->removeFromChatList(dialogs);
history->clearNotifications();
if (App::wnd()) App::wnd()->notifyClear(history);
- if (contacts.list.rowByPeer.constFind(history->peer->id) != contacts.list.rowByPeer.cend()) {
- if (contactsNoDialogs.list.rowByPeer.constFind(history->peer->id) == contactsNoDialogs.list.rowByPeer.cend()) {
- contactsNoDialogs.addByName(history);
+ if (contacts->contains(history->peer->id)) {
+ if (!contactsNoDialogs->contains(history->peer->id)) {
+ contactsNoDialogs->addByName(history);
}
}
@@ -480,14 +496,18 @@ void DialogsInner::removeDialog(History *history) {
refresh();
}
-void DialogsInner::dlgUpdated(DialogRow *row) {
+void DialogsInner::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) {
if (_state == DefaultState) {
- update(0, row->pos * st::dlgHeight, fullWidth(), st::dlgHeight);
+ if (Global::DialogsMode() == list) {
+ update(0, dialogsOffset() + row->pos() * st::dlgHeight, fullWidth(), st::dlgHeight);
+ }
} else if (_state == FilteredState || _state == SearchedState) {
- for (int32 i = 0, l = _filterResults.size(); i < l; ++i) {
- if (_filterResults.at(i)->history == row->history) {
- update(0, i * st::dlgHeight, fullWidth(), st::dlgHeight);
- break;
+ if (list == Dialogs::Mode::All) {
+ for (int32 i = 0, l = _filterResults.size(); i < l; ++i) {
+ if (_filterResults.at(i)->history() == row->history()) {
+ update(0, i * st::dlgHeight, fullWidth(), st::dlgHeight);
+ break;
+ }
}
}
}
@@ -495,15 +515,13 @@ void DialogsInner::dlgUpdated(DialogRow *row) {
void DialogsInner::dlgUpdated(History *history, MsgId msgId) {
if (_state == DefaultState) {
- DialogRow *row = 0;
- DialogsList::RowByPeer::iterator i = dialogs.list.rowByPeer.find(history->peer->id);
- if (i != dialogs.list.rowByPeer.cend()) {
- update(0, i.value()->pos * st::dlgHeight, fullWidth(), st::dlgHeight);
+ if (auto row = shownDialogs()->getRow(history->peer->id)) {
+ update(0, dialogsOffset() + row->pos() * st::dlgHeight, fullWidth(), st::dlgHeight);
}
} else if (_state == FilteredState || _state == SearchedState) {
int32 cnt = 0, add = filteredOffset();
for (FilteredDialogs::const_iterator i = _filterResults.cbegin(), e = _filterResults.cend(); i != e; ++i) {
- if ((*i)->history == history) {
+ if ((*i)->history() == history) {
update(0, add + cnt * st::dlgHeight, fullWidth(), st::dlgHeight);
break;
}
@@ -522,7 +540,7 @@ void DialogsInner::dlgUpdated(History *history, MsgId msgId) {
if (!_searchResults.isEmpty()) {
int32 cnt = 0, add = searchedOffset();
for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) {
- if ((*i)->_item->history() == history && (*i)->_item->id == msgId) {
+ if ((*i)->item()->history() == history && (*i)->item()->id == msgId) {
update(0, add + cnt * st::dlgHeight, fullWidth(), st::dlgHeight);
break;
}
@@ -542,17 +560,19 @@ void DialogsInner::updateSelectedRow(PeerData *peer) {
if (_state == DefaultState) {
if (peer) {
if (History *h = App::historyLoaded(peer->id)) {
- if (h->inChatList()) {
- update(0, h->posInChatList() * st::dlgHeight, fullWidth(), st::dlgHeight);
+ if (h->inChatList(Global::DialogsMode())) {
+ update(0, dialogsOffset() + h->posInChatList(Global::DialogsMode()) * st::dlgHeight, fullWidth(), st::dlgHeight);
}
}
- } else if (sel) {
- update(0, sel->pos * st::dlgHeight, fullWidth(), st::dlgHeight);
+ } else if (_sel) {
+ update(0, dialogsOffset() + _sel->pos() * st::dlgHeight, fullWidth(), st::dlgHeight);
+ } else if (_importantSwitchSel) {
+ update(0, 0, fullWidth(), st::dlgImportantHeight);
}
} else if (_state == FilteredState || _state == SearchedState) {
if (peer) {
for (int32 i = 0, l = _filterResults.size(); i != l; ++i) {
- if (_filterResults.at(i)->history->peer == peer) {
+ if (_filterResults.at(i)->history()->peer == peer) {
update(0, filteredOffset() + i * st::dlgHeight, fullWidth(), st::dlgHeight);
break;
}
@@ -572,10 +592,15 @@ void DialogsInner::updateSelectedRow(PeerData *peer) {
void DialogsInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
- selByMouse = false;
- if (sel || _filteredSel >= 0 || _hashtagSel >= 0 || _searchedSel >= 0 || _peopleSel >= 0) {
+ clearSelection();
+}
+
+void DialogsInner::clearSelection() {
+ _selByMouse = false;
+ if (_importantSwitchSel || _sel || _filteredSel >= 0 || _hashtagSel >= 0 || _searchedSel >= 0 || _peopleSel >= 0) {
updateSelectedRow();
- sel = 0;
+ _sel = nullptr;
+ _importantSwitchSel = false;
_filteredSel = _searchedSel = _peopleSel = _hashtagSel = -1;
setCursor(style::cur_default);
}
@@ -594,16 +619,16 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) {
if (e->reason() == QContextMenuEvent::Mouse) {
lastMousePos = e->globalPos();
- selByMouse = true;
+ _selByMouse = true;
onUpdateSelected(true);
}
History *history = 0;
if (_state == DefaultState) {
- if (sel) history = sel->history;
+ if (_sel) history = _sel->history();
} else if (_state == FilteredState || _state == SearchedState) {
if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) {
- history = _filterResults[_filteredSel]->history;
+ history = _filterResults[_filteredSel]->history();
}
}
if (!history) return;
@@ -716,7 +741,7 @@ void DialogsInner::onMenuDestroyed(QObject *obj) {
}
lastMousePos = QCursor::pos();
if (rect().contains(mapFromGlobal(lastMousePos))) {
- selByMouse = true;
+ _selByMouse = true;
setMouseTracking(true);
onUpdateSelected(true);
}
@@ -732,9 +757,12 @@ void DialogsInner::onParentGeometryChanged() {
}
void DialogsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
- dialogs.peerNameChanged(peer, oldNames, oldChars);
- contactsNoDialogs.peerNameChanged(peer, oldNames, oldChars);
- contacts.peerNameChanged(peer, oldNames, oldChars);
+ dialogs->peerNameChanged(Dialogs::Mode::All, peer, oldNames, oldChars);
+ if (importantDialogs) {
+ importantDialogs->peerNameChanged(Dialogs::Mode::Important, peer, oldNames, oldChars);
+ }
+ contactsNoDialogs->peerNameChanged(peer, oldNames, oldChars);
+ contacts->peerNameChanged(peer, oldNames, oldChars);
update();
}
@@ -775,35 +803,36 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
_state = FilteredState;
_filterResults.clear();
if (!_searchInPeer && !f.isEmpty()) {
- DialogsList *dialogsToFilter = 0, *contactsNoDialogsToFilter = 0;
- if (dialogs.list.count) {
+ const Dialogs::List *toFilter = nullptr;
+ if (!dialogs->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
- DialogsIndexed::DialogsIndex::iterator i = dialogs.index.find(fi->at(0));
- if (i == dialogs.index.cend()) {
- dialogsToFilter = 0;
+ auto found = dialogs->filtered(fi->at(0));
+ if (found->isEmpty()) {
+ toFilter = nullptr;
break;
}
- if (!dialogsToFilter || dialogsToFilter->count > i.value()->count) {
- dialogsToFilter = i.value();
+ if (!toFilter || toFilter->size() > found->size()) {
+ toFilter = found;
}
}
}
- if (contactsNoDialogs.list.count) {
+ const Dialogs::List *toFilterContacts = nullptr;
+ if (!contactsNoDialogs->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
- DialogsIndexed::DialogsIndex::iterator i = contactsNoDialogs.index.find(fi->at(0));
- if (i == contactsNoDialogs.index.cend()) {
- contactsNoDialogsToFilter = 0;
+ auto found = contactsNoDialogs->filtered(fi->at(0));
+ if (found->isEmpty()) {
+ toFilterContacts = nullptr;
break;
}
- if (!contactsNoDialogsToFilter || contactsNoDialogsToFilter->count > i.value()->count) {
- contactsNoDialogsToFilter = i.value();
+ if (!toFilterContacts || toFilterContacts->size() > found->size()) {
+ toFilterContacts = found;
}
}
}
- _filterResults.reserve((dialogsToFilter ? dialogsToFilter->count : 0) + (contactsNoDialogsToFilter ? contactsNoDialogsToFilter->count : 0));
- if (dialogsToFilter && dialogsToFilter->count) {
- for (DialogRow *i = dialogsToFilter->begin, *e = dialogsToFilter->end; i != e; i = i->next) {
- const PeerData::Names &names(i->history->peer->names);
+ _filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0));
+ if (toFilter) {
+ for_const (auto row, *toFilter) {
+ const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
@@ -817,13 +846,13 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
}
}
if (fi == fe) {
- _filterResults.push_back(i);
+ _filterResults.push_back(row);
}
}
}
- if (contactsNoDialogsToFilter && contactsNoDialogsToFilter->count) {
- for (DialogRow *i = contactsNoDialogsToFilter->begin, *e = contactsNoDialogsToFilter->end; i != e; i = i->next) {
- const PeerData::Names &names(i->history->peer->names);
+ if (toFilterContacts) {
+ for_const (auto row, *toFilterContacts) {
+ const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
@@ -837,7 +866,7 @@ void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
}
}
if (fi == fe) {
- _filterResults.push_back(i);
+ _filterResults.push_back(row);
}
}
}
@@ -913,17 +942,17 @@ void DialogsInner::peerUpdated(PeerData *peer) {
PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) {
lastMousePos = globalPos;
- selByMouse = true;
+ _selByMouse = true;
onUpdateSelected(true);
if (_state == DefaultState) {
- if (sel) return sel->history->peer;
+ if (_sel) return _sel->history()->peer;
} else if (_state == FilteredState || _state == SearchedState) {
if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) {
- return _filterResults[_filteredSel]->history->peer;
+ return _filterResults[_filteredSel]->history()->peer;
} else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) {
return _peopleResults[_peopleSel];
} else if (_searchedSel >= 0 && _searchedSel < _searchResults.size()) {
- return _searchResults[_searchedSel]->_item->history()->peer;
+ return _searchResults[_searchedSel]->item()->history()->peer;
}
}
return 0;
@@ -932,7 +961,7 @@ PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) {
void DialogsInner::itemRemoved(HistoryItem *item) {
int wasCount = _searchResults.size();
for (int i = 0; i < _searchResults.size();) {
- if (_searchResults[i]->_item == item) {
+ if (_searchResults[i]->item() == item) {
_searchResults.remove(i);
if (item->history()->peer == _searchInMigrated) {
if (_searchedMigratedCount > 0) --_searchedMigratedCount;
@@ -953,7 +982,7 @@ void DialogsInner::dialogsReceived(const QVector &added) {
History *history = 0;
switch (i->type()) {
case mtpc_dialog: {
- const MTPDdialog &d(i->c_dialog());
+ const auto &d(i->c_dialog());
history = App::historyFromDialog(peerFromMTP(d.vpeer), d.vunread_count.v, d.vread_inbox_max_id.v);
if (App::main()) {
App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, history);
@@ -961,7 +990,7 @@ void DialogsInner::dialogsReceived(const QVector &added) {
} break;
case mtpc_dialogChannel: {
- const MTPDdialogChannel &d(i->c_dialogChannel());
+ const auto &d(i->c_dialogChannel());
PeerData *peer = App::peerLoaded(peerFromMTP(d.vpeer));
int32 unreadCount = (peer && peer->isMegagroup()) ? d.vunread_count.v : d.vunread_important_count.v;
History *history = App::historyFromDialog(peerFromMTP(d.vpeer), unreadCount, d.vread_inbox_max_id.v);
@@ -990,7 +1019,7 @@ void DialogsInner::dialogsReceived(const QVector &added) {
if (!history->lastMsgDate.isNull()) {
addSavedPeersAfter(history->lastMsgDate);
}
- contactsNoDialogs.del(history->peer);
+ contactsNoDialogs->del(history->peer);
if (history->peer->migrateFrom()) {
removeDialog(App::historyLoaded(history->peer->migrateFrom()->id));
} else if (history->peer->migrateTo() && history->peer->migrateTo()->amIn()) {
@@ -1000,9 +1029,9 @@ void DialogsInner::dialogsReceived(const QVector &added) {
}
if (App::wnd()) App::wnd()->updateCounter();
- if (!sel && dialogs.list.count) {
- sel = dialogs.list.begin;
- contactSel = false;
+ if (!_sel && !shownDialogs()->isEmpty()) {
+ _sel = *shownDialogs()->cbegin();
+ _importantSwitchSel = false;
}
refresh();
}
@@ -1012,7 +1041,7 @@ void DialogsInner::addSavedPeersAfter(const QDateTime &date) {
while (!saved.isEmpty() && (date.isNull() || date < saved.lastKey())) {
History *history = App::history(saved.last()->id);
history->setChatsListDate(saved.lastKey());
- contactsNoDialogs.del(history->peer);
+ contactsNoDialogs->del(history->peer);
saved.remove(saved.lastKey(), saved.last());
}
}
@@ -1030,7 +1059,7 @@ bool DialogsInner::searchReceived(const QVector &messages, DialogsSe
HistoryItem *item = App::histories().addNewMessage(*i, NewMessageExisting);
int32 lastDate = dateFromMessage(*i);
if (lastDate) {
- _searchResults.push_back(new FakeDialogRow(item));
+ _searchResults.push_back(new Dialogs::FakeRow(item));
lastDateFound = lastDate;
if (type == DialogsSearchFromStart || type == DialogsSearchFromOffset) {
_lastSearchDate = lastDateFound;
@@ -1067,7 +1096,7 @@ void DialogsInner::peopleReceived(const QString &query, const QVector &
for (QVector::const_iterator i = people.cbegin(), e = people.cend(); i != e; ++i) {
PeerId peerId = peerFromMTP(*i);
if (History *h = App::historyLoaded(peerId)) {
- if (h->inChatList()) {
+ if (h->inChatList(Dialogs::Mode::All)) {
continue; // skip existing chats
}
}
@@ -1087,47 +1116,80 @@ void DialogsInner::contactsReceived(const QVector &contacts) {
}
}
}
- if (!sel && contactsNoDialogs.list.count && false) {
- sel = contactsNoDialogs.list.begin;
- contactSel = true;
- }
refresh();
}
void DialogsInner::notify_userIsContactChanged(UserData *user, bool fromThisApp) {
if (user->contact > 0) {
History *history = App::history(user->id);
- contacts.addByName(history);
- DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(user->id);
- if (i == dialogs.list.rowByPeer.cend()) {
- contactsNoDialogs.addByName(history);
- } else if (fromThisApp) {
- sel = i.value();
- contactSel = false;
+ contacts->addByName(history);
+ if (auto row = shownDialogs()->getRow(user->id)) {
+ if (fromThisApp) {
+ _sel = row;
+ _importantSwitchSel = false;
+ }
+ } else if (!dialogs->contains(user->id)) {
+ contactsNoDialogs->addByName(history);
}
} else {
- if (sel && sel->history->peer == user) {
- sel = 0;
+ if (_sel && _sel->history()->peer == user) {
+ _sel = nullptr;
}
- contactsNoDialogs.del(user);
- contacts.del(user);
+ contactsNoDialogs->del(user);
+ contacts->del(user);
}
refresh();
}
+void DialogsInner::notify_historyMuteUpdated(History *history) {
+ if (!importantDialogs || !history->inChatList(Dialogs::Mode::All)) return;
+
+ if (history->mute()) {
+ if (_sel && _sel->history() == history && Global::DialogsMode() == Dialogs::Mode::Important) {
+ _sel = nullptr;
+ }
+ history->removeFromChatList(Dialogs::Mode::Important, importantDialogs.get());
+ if (Global::DialogsMode() != Dialogs::Mode::Important) {
+ return;
+ }
+ refresh();
+ } else {
+ bool creating = !history->inChatList(Dialogs::Mode::Important);
+ if (creating) {
+ history->addToChatList(Dialogs::Mode::Important, importantDialogs.get());
+ }
+
+ auto changed = history->adjustByPosInChatList(Dialogs::Mode::All, dialogs.get());
+
+ if (Global::DialogsMode() != Dialogs::Mode::Important) {
+ return;
+ }
+
+ int from = dialogsOffset() + changed.movedFrom * st::dlgHeight;
+ int to = dialogsOffset() + changed.movedTo * st::dlgHeight;
+ emit dialogMoved(from, to);
+
+ if (creating) {
+ refresh();
+ } else if (_state == DefaultState && changed.movedFrom != changed.movedTo) {
+ update(0, qMin(from, to), fullWidth(), qAbs(from - to) + st::dlgHeight);
+ }
+ }
+}
+
void DialogsInner::refresh(bool toTop) {
int32 h = 0;
if (_state == DefaultState) {
- h = (dialogs.list.count/* + contactsNoDialogs.list.count*/) * st::dlgHeight;
- if (h) {
- if (!_addContactLnk.isHidden()) _addContactLnk.hide();
- } else {
+ if (shownDialogs()->isEmpty()) {
h = st::noContactsHeight;
if (cContactsReceived()) {
if (_addContactLnk.isHidden()) _addContactLnk.show();
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
}
+ } else {
+ h = dialogsOffset() + shownDialogs()->size() * st::dlgHeight;
+ if (!_addContactLnk.isHidden()) _addContactLnk.hide();
}
} else {
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
@@ -1146,11 +1208,11 @@ void DialogsInner::refresh(bool toTop) {
}
void DialogsInner::setMouseSel(bool msel, bool toTop) {
- selByMouse = msel;
- if (!selByMouse && toTop) {
+ _selByMouse = msel;
+ if (!_selByMouse && toTop) {
if (_state == DefaultState) {
- sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0));
- contactSel = !dialogs.list.count && contactsNoDialogs.list.count;
+ _sel = !shownDialogs()->isEmpty() ? *shownDialogs()->cbegin() : nullptr;
+ _importantSwitchSel = false;
} else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search
_filteredSel = _peopleSel = _searchedSel = _hashtagSel = -1;
setCursor(style::cur_default);
@@ -1212,31 +1274,39 @@ void DialogsInner::clearFilter() {
void DialogsInner::selectSkip(int32 direction) {
if (_state == DefaultState) {
- if (!sel) {
- if (dialogs.list.count && direction > 0) {
- sel = dialogs.list.begin;
- } else if (false && contactsNoDialogs.list.count && direction > 0) {
- sel = contactsNoDialogs.list.begin;
+ if (_importantSwitchSel) {
+ if (!shownDialogs()->isEmpty() && direction > 0) {
+ _sel = *shownDialogs()->cbegin();
+ _importantSwitchSel = false;
+ } else {
+ return;
+ }
+ } else if (!_sel) {
+ if (importantDialogs) {
+ _importantSwitchSel = true;
+ } else if (!shownDialogs()->isEmpty() && direction > 0) {
+ _sel = *shownDialogs()->cbegin();
} else {
return;
}
} else if (direction > 0) {
- if (sel->next->next) {
- sel = sel->next;
- } else if (false && sel->next == dialogs.list.end && contactsNoDialogs.list.count) {
- sel = contactsNoDialogs.list.begin;
- contactSel = true;
+ auto next = shownDialogs()->cfind(_sel);
+ if (++next != shownDialogs()->cend()) {
+ _sel = *next;
}
} else {
- if (sel->prev) {
- sel = sel->prev;
- } else if (false && sel == contactsNoDialogs.list.begin && dialogs.list.count) {
- sel = dialogs.list.end->prev;
- contactSel = false;
+ auto prev = shownDialogs()->cfind(_sel);
+ if (prev != shownDialogs()->cbegin()) {
+ _sel = *(--prev);
+ } else if (importantDialogs) {
+ _importantSwitchSel = true;
+ _sel = nullptr;
}
}
- int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight;
- emit mustScrollTo(fromY, fromY + st::dlgHeight);
+ if (_importantSwitchSel || _sel) {
+ int fromY = _importantSwitchSel ? 0 : (dialogsOffset() + _sel->pos() * st::dlgHeight);
+ emit mustScrollTo(fromY, fromY + st::dlgHeight);
+ }
} else if (_state == FilteredState || _state == SearchedState) {
if (_hashtagResults.isEmpty() && _filterResults.isEmpty() && _peopleResults.isEmpty() && _searchResults.isEmpty()) return;
if ((_hashtagSel < 0 || _hashtagSel >= _hashtagResults.size()) &&
@@ -1285,19 +1355,13 @@ void DialogsInner::selectSkip(int32 direction) {
void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) {
int32 fromY = -1;
if (_state == DefaultState) {
- DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(peer);
- if (i != dialogs.list.rowByPeer.cend()) {
- fromY = i.value()->pos * st::dlgHeight;
- } else if (false) {
- i = contactsNoDialogs.list.rowByPeer.constFind(peer);
- if (i != contactsNoDialogs.list.rowByPeer.cend()) {
- fromY = (i.value()->pos + dialogs.list.count) * st::dlgHeight;
- }
+ if (auto row = shownDialogs()->getRow(peer)) {
+ fromY = dialogsOffset() + row->pos() * st::dlgHeight;
}
} else if (_state == FilteredState || _state == SearchedState) {
if (msgId) {
for (int32 i = 0, c = _searchResults.size(); i < c; ++i) {
- if (_searchResults[i]->_item->history()->peer->id == peer && _searchResults[i]->_item->id == msgId) {
+ if (_searchResults[i]->item()->history()->peer->id == peer && _searchResults[i]->item()->id == msgId) {
fromY = searchedOffset() + i * st::dlgHeight;
break;
}
@@ -1305,7 +1369,7 @@ void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) {
}
if (fromY < 0) {
for (int32 i = 0, c = _filterResults.size(); i < c; ++i) {
- if (_filterResults[i]->history->peer->id == peer) {
+ if (_filterResults[i]->history()->peer->id == peer) {
fromY = filteredOffset() + (i * st::dlgHeight);
break;
}
@@ -1318,42 +1382,33 @@ void DialogsInner::scrollToPeer(const PeerId &peer, MsgId msgId) {
}
void DialogsInner::selectSkipPage(int32 pixels, int32 direction) {
- int32 toSkip = pixels / int32(st::dlgHeight);
+ int toSkip = pixels / int(st::dlgHeight);
if (_state == DefaultState) {
- if (!sel) {
- if (direction > 0 && dialogs.list.count) {
- sel = dialogs.list.begin;
- } else if (false && direction > 0 && contactsNoDialogs.list.count) {
- sel = contactsNoDialogs.list.begin;
+ if (!_sel) {
+ if (direction > 0 && !shownDialogs()->isEmpty()) {
+ _sel = *shownDialogs()->cbegin();
+ _importantSwitchSel = false;
} else {
return;
}
}
if (direction > 0) {
- while (toSkip-- && sel->next->next) {
- sel = sel->next;
- }
- if (false && toSkip >= 0 && sel->next == dialogs.list.end && contactsNoDialogs.list.count) {
- sel = contactsNoDialogs.list.begin;
- while (toSkip-- && sel->next->next) {
- sel = sel->next;
- }
- contactSel = true;
+ for (auto i = shownDialogs()->cfind(_sel), end = shownDialogs()->cend(); i != end && (toSkip--); ++i) {
+ _sel = *i;
}
} else {
- while (toSkip-- && sel->prev) {
- sel = sel->prev;
+ for (auto i = shownDialogs()->cfind(_sel), b = shownDialogs()->cbegin(); i != b && (toSkip--); --i) {
+ _sel = *i;
}
- if (toSkip >= 0 && sel == contactsNoDialogs.list.begin && dialogs.list.count) {
- sel = dialogs.list.end->prev;
- while (toSkip-- && sel->prev) {
- sel = sel->prev;
- }
- contactSel = false;
+ if (toSkip && importantDialogs) {
+ _importantSwitchSel = true;
+ _sel = nullptr;
}
}
- int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight;
- emit mustScrollTo(fromY, fromY + st::dlgHeight);
+ if (_importantSwitchSel || _sel) {
+ int fromY = (_importantSwitchSel ? 0 : (dialogsOffset() + _sel->pos() * st::dlgHeight));
+ emit mustScrollTo(fromY, fromY + st::dlgHeight);
+ }
} else {
return selectSkip(direction * toSkip);
}
@@ -1366,23 +1421,19 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) {
int32 yTo = yFrom + parentWidget()->height() * 5;
MTP::clearLoaderPriorities();
if (_state == DefaultState) {
- int32 otherStart = dialogs.list.count * st::dlgHeight;
+ int32 otherStart = shownDialogs()->size() * st::dlgHeight;
if (yFrom < otherStart) {
- dialogs.list.adjustCurrent(yFrom, st::dlgHeight);
- for (DialogRow *row = dialogs.list.current; row != dialogs.list.end && (row->pos * st::dlgHeight) < yTo; row = row->next) {
- row->history->peer->loadUserpic();
+ for (auto i = shownDialogs()->cfind(yFrom, st::dlgHeight), end = shownDialogs()->cend(); i != end; ++i) {
+ if (((*i)->pos() * st::dlgHeight) >= yTo) {
+ break;
+ }
+ (*i)->history()->peer->loadUserpic();
}
yFrom = 0;
} else {
yFrom -= otherStart;
}
yTo -= otherStart;
- if (yTo > 0) {
- contactsNoDialogs.list.adjustCurrent(yFrom, st::dlgHeight);
- for (DialogRow *row = contactsNoDialogs.list.current; row != contactsNoDialogs.list.end && (row->pos * st::dlgHeight) < yTo; row = row->next) {
- row->history->peer->loadUserpic();
- }
- }
} else if (_state == FilteredState || _state == SearchedState) {
int32 from = (yFrom - filteredOffset()) / st::dlgHeight;
if (from < 0) from = 0;
@@ -1391,7 +1442,7 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) {
if (to > _filterResults.size()) to = _filterResults.size();
for (; from < to; ++from) {
- _filterResults[from]->history->peer->loadUserpic();
+ _filterResults[from]->history()->peer->loadUserpic();
}
}
@@ -1412,17 +1463,30 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) {
if (to > _searchResults.size()) to = _searchResults.size();
for (; from < to; ++from) {
- _searchResults[from]->_item->history()->peer->loadUserpic();
+ _searchResults[from]->item()->history()->peer->loadUserpic();
}
}
}
}
bool DialogsInner::choosePeer() {
- History *history = 0;
+ History *history = nullptr;
MsgId msgId = ShowAtUnreadMsgId;
if (_state == DefaultState) {
- if (sel) history = sel->history;
+ if (_importantSwitchSel && importantDialogs) {
+ clearSelection();
+ if (Global::DialogsMode() == Dialogs::Mode::All) {
+ Global::SetDialogsMode(Dialogs::Mode::Important);
+ } else {
+ Global::SetDialogsMode(Dialogs::Mode::All);
+ }
+ Local::writeUserSettings();
+ refresh();
+ _importantSwitchSel = true;
+ return true;
+ } else if (_sel) {
+ history = _sel->history();
+ }
} else if (_state == FilteredState || _state == SearchedState) {
if (_hashtagSel >= 0 && _hashtagSel < _hashtagResults.size()) {
QString hashtag = _hashtagResults.at(_hashtagSel);
@@ -1441,7 +1505,7 @@ bool DialogsInner::choosePeer() {
Local::writeRecentHashtagsAndBots();
emit refreshHashtags();
- selByMouse = true;
+ _selByMouse = true;
onUpdateSelected(true);
} else {
saveRecentHashtags('#' + hashtag);
@@ -1450,12 +1514,12 @@ bool DialogsInner::choosePeer() {
return true;
}
if (_filteredSel >= 0 && _filteredSel < _filterResults.size()) {
- history = _filterResults[_filteredSel]->history;
+ history = _filterResults[_filteredSel]->history();
} else if (_peopleSel >= 0 && _peopleSel < _peopleResults.size()) {
history = App::history(_peopleResults[_peopleSel]->id);
} else if (_searchedSel >= 0 && _searchedSel < _searchResults.size()) {
- history = _searchResults[_searchedSel]->_item->history();
- msgId = _searchResults[_searchedSel]->_item->id;
+ history = _searchResults[_searchedSel]->item()->history();
+ msgId = _searchResults[_searchedSel]->item()->id;
}
}
if (history) {
@@ -1468,7 +1532,7 @@ bool DialogsInner::choosePeer() {
emit searchResultChosen();
}
updateSelectedRow();
- sel = 0;
+ _sel = nullptr;
_filteredSel = _peopleSel = _searchedSel = _hashtagSel = -1;
return true;
}
@@ -1504,8 +1568,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
}
void DialogsInner::destroyData() {
- sel = 0;
- contactSel = false;
+ _sel = nullptr;
_hashtagSel = -1;
_hashtagResults.clear();
_filteredSel = -1;
@@ -1513,61 +1576,49 @@ void DialogsInner::destroyData() {
_filter.clear();
_searchedSel = _peopleSel = -1;
clearSearchResults();
- contacts.clear();
- contactsNoDialogs.clear();
- dialogs.clear();
+ contacts = nullptr;
+ contactsNoDialogs = nullptr;
+ dialogs = nullptr;
+ if (importantDialogs) {
+ importantDialogs = nullptr;
+ }
}
void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const {
if (!inPeer) {
- outPeer = 0;
+ outPeer = nullptr;
outMsg = 0;
return;
}
if (_state == DefaultState) {
- DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(inPeer->id);
- if (i == dialogs.list.rowByPeer.constEnd()) {
- i = contactsNoDialogs.list.rowByPeer.constFind(inPeer->id);
- if (i == contactsNoDialogs.list.rowByPeer.cend()) {
- outPeer = 0;
- outMsg = 0;
- return;
- }
- if (i.value()->prev) {
- outPeer = i.value()->prev->history->peer;
- outMsg = ShowAtUnreadMsgId;
- return;
- } else if (dialogs.list.count) {
- outPeer = dialogs.list.end->prev->history->peer;
+ if (auto row = shownDialogs()->getRow(inPeer->id)) {
+ auto i = shownDialogs()->cfind(row);
+ if (i != shownDialogs()->cbegin()) {
+ outPeer = (*(--i))->history()->peer;
outMsg = ShowAtUnreadMsgId;
return;
}
- outPeer = 0;
- outMsg = 0;
- return;
- }
- if (i.value()->prev) {
- outPeer = i.value()->prev->history->peer;
- outMsg = ShowAtUnreadMsgId;
- return;
}
+ outPeer = nullptr;
+ outMsg = 0;
+ return;
} else if (_state == FilteredState || _state == SearchedState) {
if (inMsg && !_searchResults.isEmpty()) {
for (SearchResults::const_iterator b = _searchResults.cbegin(), i = b + 1, e = _searchResults.cend(); i != e; ++i) {
- if ((*i)->_item->history()->peer == inPeer && (*i)->_item->id == inMsg) {
+ if ((*i)->item()->history()->peer == inPeer && (*i)->item()->id == inMsg) {
SearchResults::const_iterator j = i - 1;
- outPeer = (*j)->_item->history()->peer;
- outMsg = (*j)->_item->id;
+ outPeer = (*j)->item()->history()->peer;
+ outMsg = (*j)->item()->id;
return;
}
}
- if (_searchResults.at(0)->_item->history()->peer == inPeer && _searchResults.at(0)->_item->id == inMsg) {
+ if (_searchResults.at(0)->item()->history()->peer == inPeer && _searchResults.at(0)->item()->id == inMsg) {
outMsg = ShowAtUnreadMsgId;
if (_peopleResults.isEmpty()) {
if (_filterResults.isEmpty()) {
- outPeer = 0;
+ outPeer = nullptr;
} else {
- outPeer = _filterResults.back()->history->peer;
+ outPeer = _filterResults.back()->history()->peer;
}
} else {
outPeer = _peopleResults.back();
@@ -1576,7 +1627,7 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou
}
}
if (!_peopleResults.isEmpty() && _peopleResults.at(0) == inPeer) {
- outPeer = _filterResults.isEmpty() ? 0 : _filterResults.back()->history->peer;
+ outPeer = _filterResults.isEmpty() ? 0 : _filterResults.back()->history()->peer;
outMsg = ShowAtUnreadMsgId;
return;
}
@@ -1589,65 +1640,49 @@ void DialogsInner::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&ou
}
}
}
- if (_filterResults.isEmpty() || _filterResults.at(0)->history->peer == inPeer) {
- outPeer = 0;
+ if (_filterResults.isEmpty() || _filterResults.at(0)->history()->peer == inPeer) {
+ outPeer = nullptr;
outMsg = 0;
return;
}
for (FilteredDialogs::const_iterator b = _filterResults.cbegin(), i = b + 1, e = _filterResults.cend(); i != e; ++i) {
- if ((*i)->history->peer == inPeer) {
- outPeer = (*(i - 1))->history->peer;
+ if ((*i)->history()->peer == inPeer) {
+ outPeer = (*(i - 1))->history()->peer;
outMsg = ShowAtUnreadMsgId;
return;
}
}
}
- outPeer = 0;
+ outPeer = nullptr;
outMsg = 0;
}
void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const {
if (!inPeer) {
- outPeer = 0;
+ outPeer = nullptr;
outMsg = 0;
return;
}
if (_state == DefaultState) {
- DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(inPeer->id);
- if (i == dialogs.list.rowByPeer.constEnd()) {
- //i = contactsNoDialogs.list.rowByPeer.constFind(inPeer->id);
- //if (i == contactsNoDialogs.list.rowByPeer.cend()) {
- // outPeer = 0;
- // outMsg = 0;
- // return;
- //}
- //if (i.value()->next != contactsNoDialogs.list.end) {
- // outPeer = i.value()->next->history->peer;
- // outMsg = ShowAtUnreadMsgId;
- // return;
- //}
- outPeer = 0;
- outMsg = 0;
- return;
- }
-
- if (i.value()->next != dialogs.list.end) {
- outPeer = i.value()->next->history->peer;
- outMsg = ShowAtUnreadMsgId;
- return;
- } else if (false && contactsNoDialogs.list.count) {
- outPeer = contactsNoDialogs.list.begin->history->peer;
- outMsg = ShowAtUnreadMsgId;
- return;
+ if (auto row = shownDialogs()->getRow(inPeer->id)) {
+ auto i = shownDialogs()->cfind(row) + 1;
+ if (i != shownDialogs()->cend()) {
+ outPeer = (*i)->history()->peer;
+ outMsg = ShowAtUnreadMsgId;
+ return;
+ }
}
+ outPeer = nullptr;
+ outMsg = 0;
+ return;
} else if (_state == FilteredState || _state == SearchedState) {
if (inMsg) {
for (SearchResults::const_iterator i = _searchResults.cbegin(), e = _searchResults.cend(); i != e; ++i) {
- if ((*i)->_item->history()->peer == inPeer && (*i)->_item->id == inMsg) {
+ if ((*i)->item()->history()->peer == inPeer && (*i)->item()->id == inMsg) {
++i;
- outPeer = (i == e) ? 0 : (*i)->_item->history()->peer;
- outMsg = (i == e) ? 0 : (*i)->_item->id;
+ outPeer = (i == e) ? nullptr : (*i)->item()->history()->peer;
+ outMsg = (i == e) ? 0 : (*i)->item()->id;
return;
}
}
@@ -1656,42 +1691,42 @@ void DialogsInner::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&out
if ((*i) == inPeer) {
++i;
if (i == e && !_searchResults.isEmpty()) {
- outPeer = _searchResults.front()->_item->history()->peer;
- outMsg = _searchResults.front()->_item->id;
+ outPeer = _searchResults.front()->item()->history()->peer;
+ outMsg = _searchResults.front()->item()->id;
} else {
- outPeer = (i == e) ? 0 : (*i);
+ outPeer = (i == e) ? nullptr : (*i);
outMsg = ShowAtUnreadMsgId;
}
return;
}
}
for (FilteredDialogs::const_iterator i = _filterResults.cbegin(), e = _filterResults.cend(); i != e; ++i) {
- if ((*i)->history->peer == inPeer) {
+ if ((*i)->history()->peer == inPeer) {
++i;
if (i == e && !_peopleResults.isEmpty()) {
outPeer = _peopleResults.front();
outMsg = ShowAtUnreadMsgId;
} else if (i == e && !_searchResults.isEmpty()) {
- outPeer = _searchResults.front()->_item->history()->peer;
- outMsg = _searchResults.front()->_item->id;
+ outPeer = _searchResults.front()->item()->history()->peer;
+ outMsg = _searchResults.front()->item()->id;
} else {
- outPeer = (i == e) ? 0 : (*i)->history->peer;
+ outPeer = (i == e) ? nullptr : (*i)->history()->peer;
outMsg = ShowAtUnreadMsgId;
}
return;
}
}
}
- outPeer = 0;
+ outPeer = nullptr;
outMsg = 0;
}
-DialogsIndexed &DialogsInner::contactsList() {
- return contacts;
+Dialogs::IndexedList *DialogsInner::contactsList() {
+ return contacts.get();
}
-DialogsIndexed &DialogsInner::dialogsList() {
- return dialogs;
+Dialogs::IndexedList *DialogsInner::dialogsList() {
+ return dialogs.get();
}
DialogsInner::FilteredDialogs &DialogsInner::filteredList() {
@@ -1791,19 +1826,19 @@ void DialogsWidget::activate() {
}
void DialogsWidget::createDialog(History *history) {
- bool creating = !history->inChatList();
+ bool creating = !history->inChatList(Dialogs::Mode::All);
_inner.createDialog(history);
if (creating && history->peer->migrateFrom()) {
if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) {
- if (h->inChatList()) {
+ if (h->inChatList(Dialogs::Mode::All)) {
removeDialog(h);
}
}
}
}
-void DialogsWidget::dlgUpdated(DialogRow *row) {
- _inner.dlgUpdated(row);
+void DialogsWidget::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) {
+ _inner.dlgUpdated(list, row);
}
void DialogsWidget::dlgUpdated(History *row, MsgId msgId) {
@@ -1889,21 +1924,25 @@ void DialogsWidget::notify_userIsContactChanged(UserData *user, bool fromThisApp
_inner.notify_userIsContactChanged(user, fromThisApp);
}
+void DialogsWidget::notify_historyMuteUpdated(History *history) {
+ _inner.notify_historyMuteUpdated(history);
+}
+
void DialogsWidget::unreadCountsReceived(const QVector &dialogs) {
for (QVector::const_iterator i = dialogs.cbegin(), e = dialogs.cend(); i != e; ++i) {
switch (i->type()) {
case mtpc_dialog: {
- const MTPDdialog &d(i->c_dialog());
+ const auto &d(i->c_dialog());
if (History *h = App::historyLoaded(peerFromMTP(d.vpeer))) {
App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, h);
- if (d.vunread_count.v >= h->unreadCount) {
+ if (d.vunread_count.v >= h->unreadCount()) {
h->setUnreadCount(d.vunread_count.v, false);
h->inboxReadBefore = d.vread_inbox_max_id.v + 1;
}
}
} break;
case mtpc_dialogChannel: {
- const MTPDdialogChannel &d(i->c_dialogChannel());
+ const auto &d(i->c_dialogChannel());
if (History *h = App::historyLoaded(peerFromMTP(d.vpeer))) {
if (h->peer->isChannel()) {
h->peer->asChannel()->ptsReceived(d.vpts.v);
@@ -1914,7 +1953,7 @@ void DialogsWidget::unreadCountsReceived(const QVector &dialogs) {
}
App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, h);
int32 unreadCount = h->isMegagroup() ? d.vunread_count.v : d.vunread_important_count.v;
- if (unreadCount >= h->unreadCount) {
+ if (unreadCount >= h->unreadCount()) {
h->setUnreadCount(unreadCount, false);
h->inboxReadBefore = d.vread_inbox_max_id.v + 1;
}
@@ -1932,7 +1971,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
const QVector *m = 0;
switch (dialogs.type()) {
case mtpc_messages_dialogs: {
- const MTPDmessages_dialogs &data(dialogs.c_messages_dialogs());
+ const auto &data(dialogs.c_messages_dialogs());
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
m = &data.vmessages.c_vector().v;
@@ -1940,7 +1979,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
_dialogsFull = true;
} break;
case mtpc_messages_dialogsSlice: {
- const MTPDmessages_dialogsSlice &data(dialogs.c_messages_dialogsSlice());
+ const auto &data(dialogs.c_messages_dialogsSlice());
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
m = &data.vmessages.c_vector().v;
@@ -1966,7 +2005,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
for (int32 i = v->size(); i > 0;) {
PeerId peer = 0;
MsgId msgId = 0;
- const MTPDialog &d(v->at(--i));
+ const auto &d(v->at(--i));
switch (d.type()) {
case mtpc_dialog:
msgId = d.c_dialog().vtop_message.v;
@@ -1984,7 +2023,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
if (msgId) {
if (!lastMsgId) lastMsgId = msgId;
for (int32 j = m->size(); j > 0;) {
- const MTPMessage &d(m->at(--j));
+ const auto &d(m->at(--j));
if (idFromMessage(d) == msgId && peerFromMessage(d) == peer) {
int32 date = dateFromMessage(d);
if (date) lastDate = date;
@@ -2011,7 +2050,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
}
bool DialogsWidget::dialogsFailed(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
LOG(("RPC Error: %1 %2: %3").arg(error.code()).arg(error.type()).arg(error.description()));
if (_dialogsRequest == req) {
@@ -2153,7 +2192,7 @@ void DialogsWidget::loadDialogs() {
void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &contacts) {
cSetContactsReceived(true);
if (contacts.type() == mtpc_contacts_contacts) {
- const MTPDcontacts_contacts &d(contacts.c_contacts_contacts());
+ const auto &d(contacts.c_contacts_contacts());
App::feedUsers(d.vusers);
_inner.contactsReceived(d.vcontacts.c_vector().v);
}
@@ -2161,7 +2200,7 @@ void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &contacts) {
}
bool DialogsWidget::contactsFailed(const RPCError &error) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
return true;
}
@@ -2180,10 +2219,10 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa
if (_searchRequest == req) {
switch (result.type()) {
case mtpc_messages_messages: {
- const MTPDmessages_messages &d(result.c_messages_messages());
+ const auto &d(result.c_messages_messages());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
- const QVector &msgs(d.vmessages.c_vector().v);
+ const auto &msgs(d.vmessages.c_vector().v);
if (!_inner.searchReceived(msgs, type, msgs.size())) {
if (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset) {
_searchFullMigrated = true;
@@ -2194,10 +2233,10 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa
} break;
case mtpc_messages_messagesSlice: {
- const MTPDmessages_messagesSlice &d(result.c_messages_messagesSlice());
+ const auto &d(result.c_messages_messagesSlice());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
- const QVector &msgs(d.vmessages.c_vector().v);
+ const auto &msgs(d.vmessages.c_vector().v);
if (!_inner.searchReceived(msgs, type, d.vcount.v)) {
if (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset) {
_searchFullMigrated = true;
@@ -2208,7 +2247,7 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa
} break;
case mtpc_messages_channelMessages: {
- const MTPDmessages_channelMessages &d(result.c_messages_channelMessages());
+ const auto &d(result.c_messages_channelMessages());
if (_searchInPeer && _searchInPeer->isChannel()) {
_searchInPeer->asChannel()->ptsReceived(d.vpts.v);
} else {
@@ -2220,7 +2259,7 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
- const QVector &msgs(d.vmessages.c_vector().v);
+ const auto &msgs(d.vmessages.c_vector().v);
if (!_inner.searchReceived(msgs, type, d.vcount.v)) {
if (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset) {
_searchFullMigrated = true;
@@ -2262,7 +2301,7 @@ void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId
}
bool DialogsWidget::searchFailed(DialogsSearchRequestType type, const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (_searchRequest == req) {
_searchRequest = 0;
@@ -2276,7 +2315,7 @@ bool DialogsWidget::searchFailed(DialogsSearchRequestType type, const RPCError &
}
bool DialogsWidget::peopleFailed(const RPCError &error, mtpRequestId req) {
- if (mtpIsFlood(error)) return false;
+ if (MTP::isDefaultHandledError(error)) return false;
if (_peopleRequest == req) {
_peopleRequest = 0;
@@ -2365,7 +2404,7 @@ void DialogsWidget::onListScroll() {
if (_scroll.scrollTop() > (_inner.searchList().size() + _inner.filteredList().size() + _inner.peopleList().size()) * st::dlgHeight - PreloadHeightsCount * _scroll.height()) {
onSearchMore();
}
- } else if (_scroll.scrollTop() > _inner.dialogsList().list.count * st::dlgHeight - PreloadHeightsCount * _scroll.height()) {
+ } else if (_scroll.scrollTop() > _inner.dialogsList()->size() * st::dlgHeight - PreloadHeightsCount * _scroll.height()) {
loadDialogs();
}
}
@@ -2541,11 +2580,11 @@ void DialogsWidget::removeDialog(History *history) {
onFilterUpdate();
}
-DialogsIndexed &DialogsWidget::contactsList() {
+Dialogs::IndexedList *DialogsWidget::contactsList() {
return _inner.contactsList();
}
-DialogsIndexed &DialogsWidget::dialogsList() {
+Dialogs::IndexedList *DialogsWidget::dialogsList() {
return _inner.dialogsList();
}
diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h
index 75ef10e49..fe5b33e62 100644
--- a/Telegram/SourceFiles/dialogswidget.h
+++ b/Telegram/SourceFiles/dialogswidget.h
@@ -21,6 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
class MainWidget;
+namespace Dialogs {
+class Row;
+class FakeRow;
+class IndexedList;
+} // namespace Dialogs
enum DialogsSearchRequestType {
DialogsSearchFromStart,
@@ -49,10 +54,6 @@ public:
void contactsReceived(const QVector &contacts);
- int32 filteredOffset() const;
- int32 peopleOffset() const;
- int32 searchedOffset() const;
-
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
@@ -60,14 +61,11 @@ public:
void leaveEvent(QEvent *e);
void contextMenuEvent(QContextMenuEvent *e);
- void peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const;
- void searchInPeerPaint(Painter &p, int32 w, bool onlyBackground) const;
-
void selectSkip(int32 direction);
void selectSkipPage(int32 pixels, int32 direction);
void createDialog(History *history);
- void dlgUpdated(DialogRow *row);
+ void dlgUpdated(Dialogs::Mode list, Dialogs::Row *row);
void dlgUpdated(History *row, MsgId msgId);
void removeDialog(History *history);
@@ -84,12 +82,12 @@ public:
void peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const;
void scrollToPeer(const PeerId &peer, MsgId msgId);
- typedef QVector FilteredDialogs;
+ typedef QVector